home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / ms / ms.c < prev   
C/C++ Source or Header  |  1994-10-06  |  103KB  |  3,756 lines

  1. /*
  2.  * Program:    MS - Mail System Distributed Client
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    28 November 1988
  13.  * Last Edited:    6 October 1994
  14.  *
  15.  * Copyright 1994 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. /* Version information */
  37.  
  38. static char *copyright = "\
  39.  MS user interface is:\n\
  40.   Copyright 1994 University of Washington\n\
  41.  Electronic Mail C-Client is:\n\
  42.   Copyright 1994 University of Washington\n\
  43.   Original version Copyright 1988 The Leland Stanford Junior University\n\
  44.  CCMD command interface is:\n\
  45.   Copyright 1986, 1987 Columbia University in the City of New York";
  46. static char *author = "Mark Crispin";
  47. static char *version = "7.96";
  48. static char *bug_mailbox = "MRC";
  49. static char *bug_host = "CAC.Washington.EDU";
  50. static char *hostlist[] = {    /* SMTP server host list */
  51.   "mailhost",
  52.   "localhost",
  53.   0
  54. };
  55. static char *newslist[] = {    /* News server host list */
  56.   "news",
  57.   "newshost",
  58.   "localhost",
  59.   0
  60. };
  61.  
  62. /* Parameter files */
  63.  
  64. #include <stdio.h>
  65. #include <ctype.h>
  66. #if unix
  67. #include <netdb.h>
  68. #include <signal.h>
  69. #endif
  70. #include <sys/types.h>
  71. #include "ccmd.h"
  72. #include "mail.h"
  73. #include "osdep.h"
  74. #include "smtp.h"
  75. #include "nntp.h"
  76. #include "rfc822.h"
  77. #include "misc.h"
  78.  
  79.  
  80. /* Size of temporary buffers */
  81. #define TMPLEN 1024
  82.  
  83.  
  84. /* CCMD buffer sizes */
  85. #define CMDBFL 16384
  86. #define MAXMAILBOXES 1000
  87.  
  88. /* Header sizes */
  89. #define FROMLEN 20
  90. #define SUBJECTLEN 25
  91.  
  92. /* Function prototypes */
  93.  
  94. void main (int argc,char *argv[]);
  95. void ctrlc ();
  96. short ms_init ();
  97. void toplevel (int argc,char *argv[]);
  98. void do_cmd (void (*f) (short help));
  99. void do_help (void (*f) (short help));
  100. void c_answer (short help);
  101. void c_bboard (short help);
  102. void c_blank (short help);
  103. void c_bug (short help);
  104. void c_check (short help);
  105. void c_copy (short help);
  106. void c_create (short help);
  107. void c_daytime (short help);
  108. void c_debug (short help);
  109. void c_delete (short help);
  110. void c_echo (short help);
  111. void c_exit (short help);
  112. void c_expunge (short help);
  113. void c_find (short help);
  114. void c_flag (short help);
  115. void c_forward (short help);
  116. void c_get (short help);
  117. void c_headers (short help);
  118. void c_help (short help);
  119. void c_keyword (short help);
  120. void c_kill (short help);
  121. void c_literal_type (short help);
  122. void c_mark (short help);
  123. void c_move (short help);
  124. void c_next (short help);
  125. void c_post (short help);
  126. void c_previous (short help);
  127. void c_read (short help);
  128. void c_remail (short help);
  129. void c_remove (short help);
  130. void c_rename (short help);
  131. void c_quit (short help);
  132. void c_send (short help);
  133. void c_status (short help);
  134. void c_subscribe (short help);
  135. void c_take (short help);
  136. void takelevel ();
  137. void c_type (short help);
  138. void c_unanswer (short help);
  139. void c_undelete (short help);
  140. void c_unflag (short help);
  141. void c_unkeyword (short help);
  142. void c_unmark (short help);
  143. void c_unsubscribe (short help);
  144. void c_version (short help);
  145. void r_answer (short help);
  146. void r_copy (short help);
  147. void r_delete (short help);
  148. void r_flag (short help);
  149. void r_forward (short help);
  150. void r_headers (short help);
  151. void r_help (short help);
  152. void r_keyword (short help);
  153. void r_kill (short help);
  154. void r_literal_type (short help);
  155. void r_mark (short help);
  156. void r_move (short help);
  157. void r_next (short help);
  158. void r_pipe (short help);
  159. void r_previous (short help);
  160. void r_quit (short help);
  161. void r_remail (short help);
  162. void r_status (short help);
  163. void r_type (short help);
  164. void r_unanswer (short help);
  165. void r_undelete (short help);
  166. void r_unflag (short help);
  167. void r_unkeyword (short help);
  168. void r_unmark (short help);
  169. void send_level (ENVELOPE *msg,BODY *body);
  170. void do_scmd (int (*f)(short help,ENVELOPE *msg,BODY *body),
  171.           ENVELOPE *msg,BODY *body);
  172. void do_shelp (int (*f)(short help,ENVELOPE *msg,BODY *body),
  173.            ENVELOPE *msg,BODY *body);
  174. void s_bboards (short help,ENVELOPE *msg,BODY *body);
  175. void s_bcc (short help,ENVELOPE *msg,BODY *body);
  176. void s_cc (short help,ENVELOPE *msg,BODY *body);
  177. void s_display (short help,ENVELOPE *msg,BODY *body);
  178. void s_erase (short help,ENVELOPE *msg,BODY *body);
  179. void s_help (short help,ENVELOPE *msg,BODY *body);
  180. void s_quit (short help,ENVELOPE *msg,BODY *body);
  181. void s_remove (short help,ENVELOPE *msg,BODY *body);
  182. void s_send (short help,ENVELOPE *msg,BODY *body);
  183. void s_subject (short help,ENVELOPE *msg,BODY *body);
  184. void s_to (short help,ENVELOPE *msg,BODY *body);
  185. char do_sequence (char *def);
  186. void more (void (*f)(FILE *pipe,long arg),long arg);
  187. void do_display (FILE *pipe,long i);
  188. void do_header (FILE *pipe,long i);
  189. void do_get (char *mailbox);
  190. void do_status (MAILSTREAM *stream);
  191. void do_version ();
  192. void answer_message (int msgno,int allflag);
  193. void header_message (FILE *file,long msgno);
  194. void literal_type_message (FILE *file,long msgno);
  195. void remail_message (int msgno,ADDRESS *adr);
  196. void type_message (FILE *file,long msgno);
  197. void type_address (FILE *file,char *tag,ADDRESS *adr);
  198. void type_string (FILE *file,int *pos,char *str1,char chr,char *str2);
  199. ENVELOPE *send_init ();
  200. ADDRESS *copy_adr (ADDRESS *adr,ADDRESS *ret);
  201. ADDRESS *remove_adr (ADDRESS *adr,char *text);
  202. BODY *send_text ();
  203. void send_message (ENVELOPE *msg,BODY *body);
  204. int onoff ();
  205. void cmerr (char *string);
  206.  
  207. /* Global variables */
  208.  
  209. static char sequence[CMDBFL];    /* string representing sequence */
  210. static long current = 0;    /* current message for NEXT, PREVIOUS, KILL */
  211. static char cmdbuf[CMDBFL];    /* command buffer */
  212. static char atmbuf[CMDBFL];    /* atom buffer */
  213. static char wrkbuf[CMDBFL];    /* work buffer */
  214. static keywrd mbx[MAXMAILBOXES];/* mailbox list */
  215. static keytab mbxtab = {0,mbx};    /* pointer to mailbox list */
  216. static keywrd bbd[MAXMAILBOXES];/* mailbox list */
  217. static keytab bbdtab = {0,bbd};    /* pointer to bboard list */
  218. static int critical = NIL;    /* in critical code flag */
  219. static int done = NIL;        /* exit flag */
  220. static int quitted = NIL;    /* quitted from send flag */
  221. static int debug = NIL;        /* debugging flag */
  222. static char *curhst = NIL;    /* current host */
  223. static char *curusr = NIL;    /* current login user */
  224. static char *personal = NIL;    /* user's personal name */
  225. static int nmsgs = 0;        /* local number of messages */
  226. static MAILSTREAM *stream = NIL;/* current mail stream */
  227. static char *fwdhdr = "\n ** Begin Forwarded Message(s) **\n\n";
  228. static keywrd flags[NUSERFLAGS];/* user flag table */
  229. static keytab flgtab = {0,flags};
  230.  
  231. /* Top level command table */
  232.  
  233. static keywrd cmds[] = {
  234.   {"ANSWER",    NIL,    (keyval) c_answer},
  235.   {"BBOARD",    NIL,    (keyval) c_bboard},
  236.   {"BLANK",    NIL,    (keyval) c_blank},
  237.   {"BUG",    NIL,    (keyval) c_bug},
  238.   {"CHECK",    NIL,    (keyval) c_check},
  239.   {"COPY",    NIL,    (keyval) c_copy},
  240.   {"CREATE",    NIL,    (keyval) c_create},
  241.   {"D",        KEY_INV|KEY_ABR,(keyval) 10},
  242.   {"DAYTIME",    NIL,    (keyval) c_daytime},
  243.   {"DEBUG",    NIL,    (keyval) c_debug},
  244.   {"DELETE",    NIL,    (keyval) c_delete},
  245.   {"ECHO",    NIL,    (keyval) c_echo},
  246.   {"EX",    KEY_INV|KEY_ABR,(keyval) 13},
  247.   {"EXIT",    NIL,    (keyval) c_exit},
  248.   {"EXPUNGE",    NIL,    (keyval) c_expunge},
  249.   {"FIND",    NIL,    (keyval) c_find},
  250.   {"FLAG",    NIL,    (keyval) c_flag},
  251.   {"FORWARD",    NIL,    (keyval) c_forward},
  252.   {"GET",    NIL,    (keyval) c_get},
  253.   {"H",        KEY_INV|KEY_ABR,(keyval) 20},
  254.   {"HEADERS",    NIL,    (keyval) c_headers},
  255.   {"HELP",    NIL,    (keyval) c_help},
  256.   {"KEYWORD",    NIL,    (keyval) c_keyword},
  257.   {"KILL",    NIL,    (keyval) c_kill},
  258.   {"LITERAL-TYPE",NIL,    (keyval) c_literal_type},
  259.   {"MARK",    NIL,    (keyval) c_mark},
  260.   {"MOVE",    NIL,    (keyval) c_move},
  261.   {"NEXT",    NIL,    (keyval) c_next},
  262.   {"POST",    NIL,    (keyval) c_post},
  263.   {"PREVIOUS",    NIL,    (keyval) c_previous},
  264.   {"QUIT",    NIL,    (keyval) c_quit},
  265.   {"R",        KEY_INV|KEY_ABR,(keyval) 32},
  266.   {"READ",    NIL,    (keyval) c_read},
  267.   {"REMAIL",    NIL,    (keyval) c_remail},
  268.   {"REMOVE",    NIL,    (keyval) c_remove},
  269.   {"RENAME",    NIL,    (keyval) c_rename},
  270.   {"S",        KEY_INV|KEY_ABR,(keyval) 37},
  271.   {"SEND",    NIL,    (keyval) c_send},
  272.   {"SUBSCRIBE",    NIL,    (keyval) c_subscribe},
  273.   {"STATUS",    NIL,    (keyval) c_status},
  274.   {"T",        KEY_INV|KEY_ABR,(keyval) 42},
  275.   {"TAKE",    NIL,    (keyval) c_take},
  276.   {"TYPE",    NIL,    (keyval) c_type},
  277.   {"UNANSWER",    NIL,    (keyval) c_unanswer},
  278.   {"UNDELETE",    NIL,    (keyval) c_undelete},
  279.   {"UNFLAG",    NIL,    (keyval) c_unflag},
  280.   {"UNKEYWORD",    NIL,    (keyval) c_unkeyword},
  281.   {"UNMARK",    NIL,    (keyval) c_unmark},
  282.   {"UNSUBSCRIBE",NIL,    (keyval) c_unsubscribe},
  283.   {"VERSION",    NIL,    (keyval) c_version},
  284. };
  285. static keytab cmdtab = {(sizeof (cmds)/sizeof (keywrd)),cmds};
  286.  
  287. /* Main program */
  288.  
  289. void main (int argc,char *argv[])
  290. {
  291. #if unix
  292.   signal (SIGINT,ctrlc);    /* set up CTRL/C handler */
  293.   signal (SIGQUIT,SIG_IGN);    /* ignore quit and pipe signals */
  294.   signal (SIGPIPE,SIG_IGN);
  295. #endif
  296.                 /* initialize CCMD */
  297.   cmbufs (cmdbuf,CMDBFL,atmbuf,CMDBFL,wrkbuf,CMDBFL);
  298.   cmseti (stdin,stdout,stderr);
  299.   if (ms_init () && argc <= 1) {/* initialize global state and if OK... */
  300.     cmcls ();            /* blank the screen */
  301.     do_version ();        /* display version */
  302.     do_get ("INBOX");        /* get INBOX */
  303.   }
  304.   toplevel (argc,argv);        /* enter top level cmd parser */
  305.   stream = mail_close (stream);    /* punt mail stream */
  306.   critical = T;            /* don't allow CTRL/C any more */
  307.   cmdone ();            /* restore the world */
  308.   exit (0);            /* all done */
  309. }
  310.  
  311.  
  312. #if unix
  313. /* Respond to CTRL/C
  314.  */
  315.  
  316. void ctrlc ()
  317. {
  318.   cmxprintf ("^C\n");        /* make sure user gets confirmation */
  319.   if (!critical) {        /* must not be critical */
  320.     if (!done) done = T;    /* if global done not set, set it now */
  321.     else {            /* else two in a row kill us */
  322.       cmdone ();        /* restore the world */
  323.       _exit (0);
  324.     }
  325.   }
  326. }
  327. #endif
  328.  
  329. /* Initialize MS global state
  330.  * Returns: T if OK, NIL if error
  331.  */
  332.  
  333. short ms_init ()
  334. {
  335. #if unix
  336.   char tmp[TMPLEN];
  337.   char *name;
  338.   char *suffix;
  339.   struct passwd *pwd;
  340.   struct hostent *host_name;
  341. #endif
  342.   if (curhst) fs_give ((void **) &curhst);
  343.   if (curusr) fs_give ((void **) &curusr);
  344. #if unix
  345.   gethostname (tmp,TMPLEN);    /* get local name */
  346.                 /* get it in full form */
  347.   curhst = (host_name = gethostbyname (tmp)) ?
  348.     cpystr (host_name->h_name) : cpystr (tmp);
  349.                 /* get user name and passwd entry */
  350.   if (name = (char *) getlogin ()) pwd = getpwnam (name);
  351.   else {
  352.     pwd = getpwuid (getuid ());    /* get it this way if detached, etc */
  353.     name = pwd->pw_name;
  354.   }
  355.   curusr = cpystr (name);    /* current user is this name */
  356.   if (!personal) {        /* this is OK to do only once */
  357.     strcpy (tmp,pwd->pw_gecos);    /* probably not necessay but be safe */
  358.                 /* dyke out the office and phone poop */
  359.     if (suffix = (char *) strchr (tmp,',')) suffix[0] = '\0';
  360.     personal = cpystr (tmp);    /* make a permanent copy of it */
  361.   }
  362. #else
  363.   curhst = cpystr ("somewhere");
  364.   curusr = cpystr ("unknown");
  365.   personal = NIL;
  366. #endif
  367.   flgtab._ktcnt = 0;        /* init keyword table */
  368. #include "linkage.c"
  369.   mail_parameters (NIL,SET_PREFETCH,NIL);
  370.   mm_mailbox ("INBOX");        /* INBOX is always known!! */
  371.   mail_find (NIL,"*");        /* find local mailboxes */
  372.   mail_find_bboards (NIL,"*");    /* find local bboards */
  373.   return T;
  374. }
  375.  
  376. /* Top-level command parser
  377.  * Accepts: argument count
  378.  *        argument vector
  379.  */
  380.  
  381. void toplevel (int argc,char *argv[])
  382. {
  383.   int i;
  384.   pval parseval;
  385.   fdb *used;
  386.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(cmdtab),"Command, ",NIL,NIL};
  387.   static fdb numfdb = {_CMNUM,CM_SDH,NIL,(pdat) 10,"Message number",NIL,NIL};
  388.   while (!done) {        /* until program wants to exit */
  389.     cmseter ();            /* set error trap */
  390.                 /* exit on EOF */
  391.     if (cmcsb._cmerr == CMxEOF) break;
  392.                 /* exit afterwards if command line argument */
  393.     if (cmargs (argc,argv)) done = T;
  394.     else prompt ("MS>");    /* prompt */
  395.     cmsetrp ();            /* set reparse trap */
  396.                 /* allow message number if have a stream */
  397.     cmdfdb._cmlst = stream ? (fdb *) &numfdb : NIL;
  398.                 /* parse command */
  399.     parse (&cmdfdb,&parseval,&used);
  400.     if (used == &numfdb) {    /* check for valid message number */
  401.       if (parseval._pvint >= 1 && parseval._pvint <= nmsgs) {
  402.     confirm ();        /* confirm single message */
  403.                 /* set up as current */
  404.     current = parseval._pvint;
  405.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  406.     mail_elt (stream,current)->spare = T;
  407.     sprintf (sequence,"%d",current);
  408.                 /* now output it */
  409.     more (type_message,current);
  410.       }
  411.       else cmerr ("Invalid message number");
  412.     }
  413.                 /* else do the command */
  414.     else do_cmd ((void *) parseval._pvint);
  415.   }
  416. }
  417.  
  418. /* Execute command
  419.  * Accepts: function
  420.  */
  421.  
  422. void do_cmd (void (*f) (short help))
  423. {
  424.   (*f)((short) NIL);        /* call function with help flag off */
  425. }
  426.  
  427.  
  428. /* Execute help for command
  429.  * Accepts: function
  430.  */
  431.  
  432. void do_help (void (*f) (short help))
  433. {
  434.   (*f)((short) T);        /* call function with help flag on */
  435. }
  436.  
  437. /* Top level commands */
  438.  
  439.  
  440. /* ANSWER command
  441.  * Accepts: help flag
  442.  */
  443.  
  444. static keywrd anscmds[] = {
  445.   {"ALL",    NIL,    (keyval) T},
  446.   {"SENDER-ONLY",NIL,    (keyval) NIL},
  447. };
  448. static keytab anstab = {(sizeof (anscmds)/sizeof (keywrd)),anscmds};
  449.  
  450. void c_answer (short help)
  451. {
  452.   if (help) cmxprintf ("\
  453. The ANSWER command composes and sends an answer to the specified messages.\n");
  454.   else {
  455.     char tmp[TMPLEN];
  456.     int i;
  457.     int msgno;
  458.     pval parseval;
  459.     fdb *used;
  460.     static fdb ansfdb = {_CMKEY,NIL,NIL,(pdat) &(anstab),"Answer option, ",
  461.                "SENDER-ONLY",NIL};
  462.     if (do_sequence (NIL)) for (msgno = 1; msgno <= nmsgs; ++msgno)
  463.       if (mail_elt (stream,msgno)->spare) {
  464.     current = msgno;    /* this is new current message */
  465.     cmseter ();        /* set error trap */
  466.     sprintf (tmp,"Send answer for message %d to: ",msgno);
  467.     prompt (tmp);        /* get reply option */
  468.     cmsetrp ();        /* set reparse trap */
  469.     parse (&ansfdb,&parseval,&used);
  470.     i = parseval._pvint;    /* save user's selection */
  471.     confirm ();
  472.     answer_message (msgno,i);
  473.       }
  474.   }
  475. }
  476.  
  477. /* BBOARD command
  478.  * Accepts: help flag
  479.  */
  480.  
  481. void c_bboard (short help)
  482. {
  483.   if (help) cmxprintf ("Establish connection to a bboard.\n");
  484.   else {
  485.     char tmp[TMPLEN];
  486.     pval parseval;
  487.     fdb *used;
  488.     static brktab mbxbrk = {
  489.       {                /* 1st char break array */
  490.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  491.     0x80,0x00,0x00,0x0b,0x80,0x00,0x00,0x0b
  492.       },
  493.       {                /* subsequent char break array */
  494.                 /* same as above, plus dots */
  495.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  496.     0x80,0x00,0x00,0x0b,0x80,0x00,0x00,0x0b
  497.       }
  498.     };
  499.     static fdb mb2fdb = {_CMFLD,CM_SDH,NIL,NIL,"new bboard name",NIL,&mbxbrk};
  500.     static fdb mbxfdb = {_CMKEY,NIL,&mb2fdb,(pdat)& (bbdtab),"known bboard, ",
  501.              NIL,&mbxbrk};
  502.                 /* default is general */
  503.     mbxfdb._cmdef =  "general";
  504.     noise ("NAME");
  505.                 /* parse the mailbox name */
  506.     parse (&mbxfdb,&parseval,&used);
  507.     sprintf (tmp,"*%s",atmbuf);
  508.     confirm ();
  509.     do_get (tmp);        /* get this bboard */
  510.   }
  511. }
  512.  
  513.  
  514. /* BLANK command
  515.  * Accepts: help flag
  516.  */
  517.  
  518. void c_blank (short help)
  519. {
  520.   if (help) cmxprintf ("Blanks the screen.\n");
  521.   else {
  522.     noise ("SCREEN");
  523.     confirm ();
  524.     cmcls ();            /* zap the screen */
  525.   }
  526. }
  527.  
  528. /* BUG command
  529.  * Accepts: help flag
  530.  */
  531.  
  532. void c_bug (short help)
  533. {
  534.   if (help) cmxprintf ("Report an MS bug.  This sends a message to %s@%s\n",
  535.                bug_mailbox,bug_host);
  536.   else {
  537.     char tmp[TMPLEN];
  538.     ENVELOPE *msg = NIL;
  539.     BODY *body = NIL;
  540.     pval parseval;
  541.     fdb *used;
  542.     static para_data pd = {NIL,NIL};
  543.     static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  544.     parafdb._cmdat = (pdat) &pd;
  545.     noise ("REPORT");
  546.     confirm ();
  547.     msg = send_init ();        /* make a message block */
  548.     msg->to = mail_newaddr ();    /* set up to-list */
  549.     msg->to->personal = cpystr ("MS maintainer");
  550.     msg->to->mailbox = cpystr (bug_mailbox);
  551.     msg->to->host = cpystr (bug_host);
  552.                 /* set up subject */
  553.     sprintf (tmp,"Bug in MS %s",version);
  554.     msg->subject = cpystr (tmp);
  555.     if (body = send_text ()) {    /* get text and send message */
  556.       send_level (msg,body);
  557.       mail_free_body (&body);
  558.     }
  559.     mail_free_envelope (&msg);    /* flush the message */
  560.   }
  561. }
  562.  
  563.  
  564. /* CHECK command
  565.  * Accepts: help flag
  566.  */
  567.  
  568. void c_check (short help)
  569. {
  570.   int lnmsgs;
  571.   if (help) cmxprintf ("\
  572. The CHECK command checks to see if any new messages have arrived in the\n\
  573. current mailbox.\n");
  574.   else {
  575.     if (stream) {
  576.       noise ("FOR NEW MESSAGES");
  577.       confirm ();
  578.       lnmsgs = nmsgs;        /* remember old value */
  579.       mail_check (stream);    /* check for new messages */
  580.                 /* report no change */
  581.       if (lnmsgs == nmsgs) cmxprintf ("There are no new messages\n");
  582.     }
  583.     else cmerr ("No mailbox is currently open");
  584.   }
  585. }
  586.  
  587. /* COPY command
  588.  * Accepts: help flag
  589.  */
  590.  
  591. void c_copy (short help)
  592. {
  593.   if (help) cmxprintf ("\
  594. The COPY command copies the specified messages into the specified mailbox.\n");
  595.   else {
  596.     char tmp[TMPLEN];
  597.     pval parseval;
  598.     fdb *used;
  599.     static brktab mbxbrk = {
  600.       {                /* 1st char break array */
  601.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  602.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  603.       },
  604.       {                /* subsequent char break array */
  605.                 /* same as above, plus dots */
  606.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  607.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  608.       }
  609.     };
  610.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox on this server",
  611.                "INBOX",&mbxbrk};
  612.     if (stream) {
  613.       noise ("TO MAILBOX");
  614.                 /* parse the mailbox name */
  615.       parse (&mbxfdb,&parseval,&used);
  616.       strcpy (tmp,atmbuf);    /* note the mailbox name */
  617.       if (do_sequence (NIL)) mail_copy (stream,sequence,tmp);
  618.     }
  619.     else cmerr ("No mailbox is currently open");
  620.   }
  621. }
  622.  
  623. /* CREATE command
  624.  * Accepts: help flag
  625.  */
  626.  
  627. void c_create (short help)
  628. {
  629.   if (help) cmxprintf ("Creates a new mailbox.\n");
  630.   else {
  631.     char tmp[TMPLEN];
  632.     pval parseval;
  633.     fdb *used;
  634.     static brktab mbxbrk = {
  635.       {                /* 1st char break array */
  636.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  637.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  638.       },
  639.       {                /* subsequent char break array */
  640.                 /* same as above, plus dots */
  641.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  642.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  643.       }
  644.     };
  645.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox","INBOX",&mbxbrk};
  646.     noise ("NEW MAILBOX NAMED");
  647.                 /* parse the mailbox name */
  648.     parse (&mbxfdb,&parseval,&used);
  649.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  650.     confirm ();
  651.     mail_create (mail_open (NIL,(tmp[0] == '{') ? tmp : "INBOX",OP_PROTOTYPE),
  652.          tmp);
  653.   }
  654. }
  655.  
  656.  
  657. /* DAYTIME command
  658.  * Accepts: help flag
  659.  */
  660.  
  661. void c_daytime (short help)
  662. {
  663.   if (help) cmxprintf ("Types the current date and time.\n");
  664.   else {
  665.     char tmp[TMPLEN];
  666.     confirm ();
  667.     rfc822_date (tmp);
  668.     cmxprintf (" %s\n",tmp);
  669.   }
  670. }
  671.  
  672. /* DEBUG command
  673.  * Accepts: help flag
  674.  */
  675.  
  676. void c_debug (short help)
  677. {
  678.   int state;
  679.   if (help) cmxprintf ("\
  680. The DEBUG command enables debugging information, e.g. protocol telemetry.\n");
  681.   else {
  682.     noise ("PROTOCOL");
  683.     state = onoff ();
  684.     confirm ();
  685.     debug = state;        /* set debugging */
  686.     if (stream) {        /* and frob the stream if one's there */
  687.       if (debug) mail_debug (stream);
  688.       else mail_nodebug (stream);
  689.     }
  690.   }
  691. }
  692.  
  693.  
  694. /* DELETE command
  695.  * Accepts: help flag
  696.  */
  697.  
  698. void c_delete (short help)
  699. {
  700.   if (help) cmxprintf ("\
  701. The DELETE command makes the specified messages be deleted (marked for\n\
  702. removal by a subsequent EXIT or EXPUNGE command).\n");
  703.   else if (do_sequence (NIL)) mail_setflag (stream,sequence,"\\Deleted");
  704. }
  705.  
  706.  
  707. /* ECHO command
  708.  * Accepts: help flag
  709.  */
  710.  
  711. void c_echo (short help)
  712. {
  713.   if (help) cmxprintf ("\
  714. The ECHO command takes a text line as an argument and outputs it to the\n\
  715. terminal.  This is useful in TAKE files.\n");
  716.   else {
  717.     pval parseval;
  718.     fdb *used;
  719.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"text to echo back",NIL,NIL};
  720.     parse (&linfdb,&parseval,&used);
  721.     confirm ();
  722.     cmxprintf ("%s\n",atmbuf);
  723.   }
  724. }
  725.  
  726. /* EXIT command
  727.  * Accepts: help flag
  728.  */
  729.  
  730. void c_exit (short help)
  731. {
  732.   if (help) cmxprintf ("\
  733. The EXIT command expunges the current mailbox, closes the mailbox, and\n\
  734. exits this program.\n");
  735.   else {
  736.     noise ("PROGRAM");
  737.     confirm ();
  738.                 /* expunge if a stream is open */
  739.     if (stream) mail_expunge (stream);
  740.     done = T;            /* let top level know it's time to die */
  741.   }
  742. }
  743.  
  744.  
  745. /* EXPUNGE command
  746.  * Accepts: help flag
  747.  */
  748.  
  749. void c_expunge (short help)
  750. {
  751.   int i;
  752.   if (help) cmxprintf ("\
  753. The EXPUNGE command expunges (permanently removes all deleted messages from)\n\
  754. the current mailbox.\n");
  755.   else {
  756.     if (stream) {
  757.       noise ("MAILBOX");
  758.       confirm ();
  759.       mail_expunge (stream);    /* smash the deleted messages */
  760.                 /* invalidate the current sequence */
  761.       for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  762.       sequence[current = 0] = '\0';
  763.     }
  764.     else cmerr ("No mailbox is currently open");
  765.   }
  766. }
  767.  
  768. /* FIND command
  769.  * Accepts: help flag
  770.  */
  771.  
  772. static keywrd findcmds[] = {
  773.   {"FIRST",    NIL,    (keyval) NIL},
  774.   {"NEXT",    NIL,    (keyval) T}
  775. };
  776. static keytab findtab = {(sizeof (findcmds)/sizeof (keywrd)),findcmds};
  777.  
  778.  
  779. void c_find (short help)
  780. {
  781.   if (help) cmxprintf ("Find a bboard with recent mail.\n");
  782.   else {
  783.     int i;
  784.     char tmp[TMPLEN];
  785.     char *s,lsthst[TMPLEN];
  786.     pval parseval;
  787.     fdb *used;
  788.     static fdb findfdb = {_CMKEY,NIL,NIL,(pdat) &(findtab),"what to find, ",
  789.                 NIL,NIL};
  790.     findfdb._cmdef = (stream && *stream->mailbox == '*') ? "NEXT" : "FIRST";
  791.     parse (&findfdb,&parseval,&used);
  792.     i = parseval._pvint;    /* save user's selection */
  793.     noise ("BBOARD WITH NEW MAIL");
  794.     confirm ();
  795.                 /* start after current if NEXT specified */
  796.     if (i && stream && *stream->mailbox == '*')
  797.       for (i = 0; i < bbdtab._ktcnt;)
  798.     if (!strcmp (stream->mailbox + 1,bbd[i++]._kwkwd)) break;
  799.     if (stream && (s = (*stream->mailbox == '{') ? stream->mailbox :
  800.            (((*stream->mailbox == '*')&&(stream->mailbox[1] == '{')) ?
  801.             stream->mailbox + 1 : NIL))) {
  802.       strcpy (lsthst,s);    /* copy last host */
  803.       if (s = strchr (lsthst,'}')) s[1] = '\0';
  804.     }
  805.     else lsthst[0] = '\0';    /* no last host */
  806.     sequence[current = 0] = '\0';
  807.     flgtab._ktcnt = 0;        /* re-init keyword table */
  808.     while (i < bbdtab._ktcnt) {    /* try this bboard */
  809.       nmsgs = 0;        /* re-init number of messages */
  810.       sprintf (tmp,"*%s",bbd[i++]._kwkwd);
  811.                 /* specifies silence from driver... */
  812.       if ((stream = mail_open (stream,tmp,OP_SILENT)) && stream->recent) {
  813.     nmsgs = stream->nmsgs;    /* silence option doesn't call mm_exists */
  814.     stream->silent = NIL;    /* silent no more */
  815.     do_status (stream);    /* report status of mailbox */
  816.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  817.                 /* copy keywords to our table */
  818.     for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) {
  819.                 /* value and keyword are the same */
  820.       flags[i]._kwval = (keyval) (flags[i]._kwkwd = stream->user_flags[i]);
  821.       flags[i]._kwflg = NIL;/* no special flags */
  822.     }
  823.     flgtab._ktcnt = i;    /* update keyword count */
  824.     if (*stream->mailbox == '{' || ((*stream->mailbox == '*') &&
  825.                     (stream->mailbox[1] == '{'))) {
  826.                 /* avoid duplicating find if same host */
  827.       strcpy (tmp,strchr (stream->mailbox,'{'));
  828.       if (s = strchr (tmp,'}')) s[1] = '\0';
  829.       if (strcmp (tmp,lsthst)) {
  830.         sprintf (lsthst,"%s*",tmp);
  831.         mail_find (stream,lsthst);
  832.         mail_find_bboards (stream,lsthst);
  833.       }
  834.     }
  835.     return;            /* all done */
  836.       }
  837.                 /* do a find if about to fall off the end */
  838.       else if (stream && (i == bbdtab._ktcnt)) {
  839.     if (stream->mailbox[1] == '{') {
  840.       strcpy (tmp,stream->mailbox + 1);
  841.       if (s = strchr (tmp,'}')) s[1] = '\0';
  842.       strcat (tmp,"*");
  843.     }
  844.     else strcpy (tmp,"*");
  845.     mail_find_bboards (stream,tmp);
  846.       }
  847.     }
  848.                 /* punt stream */
  849.     stream = mail_close (stream);
  850.     cmxprintf ("%%No more BBoards with new mail\n");
  851.   }
  852. }
  853.  
  854. /* FLAG command
  855.  * Accepts: help flag
  856.  */
  857.  
  858. void c_flag (short help)
  859. {
  860.   if (help) cmxprintf ("\
  861. The FLAG command makes the specified messages be flagged as urgent.\n");
  862.   else if (do_sequence (NIL)) mail_setflag (stream,sequence,"\\Flagged");
  863. }
  864.  
  865. /* FORWARD command
  866.  * Accepts: help flag
  867.  */
  868.  
  869. void c_forward (short help)
  870. {
  871.   if (help) cmxprintf ("\
  872. Forwards the specified messages with optional comments to another mailbox.\n");
  873.   else {
  874.     char tmp[TMPLEN];
  875.     int i,j;
  876.     int msgno;
  877.     char *text;
  878.     char *s;
  879.     ENVELOPE *msg;
  880.     BODY *body;
  881.     pval parseval;
  882.     fdb *used;
  883.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  884.     ADDRESS *adr = NIL;
  885.     linfdb._cmhlp = "list of forward recipients in RFC 822 format";
  886.     if (do_sequence (NIL)) {
  887.       while (!adr) {
  888.     cmseter ();        /* set error trap */
  889.     prompt ("To: ");    /* get recipient list */
  890.     cmsetrp ();        /* set reparse trap */
  891.     parse (&linfdb,&parseval,&used);
  892.                 /* parse recipient */
  893.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  894.     confirm ();
  895.       }
  896.       msg = send_init ();    /* get message block */
  897.       msg->to = adr;        /* set to-list */
  898.       if (body = send_text ()) {/* get initial text of comments */
  899.     fs_resize ((void **) &body->contents.text,
  900.            (i = strlen ((char *) body->contents.text)) +
  901.            (j = strlen (fwdhdr)));
  902.                 /* append the forward header */
  903.     memcpy (body->contents.text + i,fwdhdr,j);
  904.     for (msgno = 1; msgno <= nmsgs; ++msgno)
  905.       if (mail_elt (stream,msgno)->spare) {
  906.         if (!msg->subject) {/* if no subject yet */
  907.           tmp[0] = '[';    /* build string */
  908.                 /* get short from sans trailing spaces */
  909.           mail_fetchfrom (tmp+1,stream,msgno,FROMLEN);
  910.           for (s = tmp+FROMLEN; *s == ' '; --s) *s = '\0';
  911.           *++s = ':'; *++s = ' ';
  912.           strcpy (++s,(mail_fetchstructure (stream,msgno,NIL))->subject);
  913.           msg->subject = cpystr (tmp);
  914.         }
  915.         i += j;        /* current text size */
  916.         mail_parameters (NIL,SET_PREFETCH,(void *) T);
  917.                 /* get header of message */
  918.         text = mail_fetchheader (stream,msgno);
  919.         mail_parameters (NIL,SET_PREFETCH,NIL);
  920.                 /* resize the forward text */
  921.         fs_resize ((void **) &body->contents.text,i + (j = strlen (text)));
  922.                 /* append the forward message text */
  923.         memcpy (body->contents.text+i,text,j);
  924.         i += j;        /* current text size */
  925.                 /* get text of message */
  926.         text = mail_fetchtext (stream,msgno);
  927.                 /* resize the forward text */
  928.         fs_resize ((void **) &body->contents.text,i + (j = strlen (text)));
  929.                 /* append the forward message text */
  930.         memcpy (body->contents.text+i,text,j);
  931.       }
  932.     send_level (msg,body);    /* enter send-level */
  933.     mail_free_body (&body);
  934.       }
  935.       mail_free_envelope (&msg);/* flush the message */
  936.     }
  937.   }
  938. }
  939.  
  940. /* GET command
  941.  * Accepts: help flag
  942.  */
  943.  
  944. void c_get (short help)
  945. {
  946.   if (help) cmxprintf ("Establish connection to a mailbox.\n");
  947.   else {
  948.     char tmp[TMPLEN];
  949.     pval parseval;
  950.     fdb *used;
  951.     static brktab mbxbrk = {
  952.       {                /* 1st char break array */
  953.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  954.     0x80,0x00,0x00,0x0b,0x80,0x00,0x00,0x0b
  955.       },
  956.       {                /* subsequent char break array */
  957.                 /* same as above, plus dots */
  958.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  959.     0x80,0x00,0x00,0x0b,0x80,0x00,0x00,0x0b
  960.       }
  961.     };
  962.     static fdb mb2fdb = {_CMFLD,CM_SDH,NIL,NIL,"new mailbox name",NIL,&mbxbrk};
  963.     static fdb mbxfdb = {_CMKEY,NIL,&mb2fdb,(pdat)& (mbxtab),"known mailbox, ",
  964.              NIL,&mbxbrk};
  965.                 /* default is current mailbox or INBOX */
  966.     mbxfdb._cmdef = stream ? stream->mailbox : "INBOX";
  967.     noise ("MAILBOX");
  968.                 /* parse the mailbox name */
  969.     parse (&mbxfdb,&parseval,&used);
  970.     strcpy (tmp,atmbuf);
  971.     confirm ();
  972.     do_get (tmp);        /* get this mailbox */
  973.   }
  974. }
  975.  
  976. /* HEADERS command
  977.  * Accepts: help flag
  978.  */
  979.  
  980. void c_headers (short help)
  981. {
  982.   if (help) cmxprintf ("\
  983. The HEADERS command displays one-line summaries of the specified messages.\n");
  984.   else {
  985.     mail_parameters (NIL,SET_PREFETCH,(void *) T);
  986.                 /* parse sequence */
  987.     if (do_sequence (NIL)) more (do_header,NIL);
  988.     mail_parameters (NIL,SET_PREFETCH,NIL);
  989.   }
  990. }
  991.  
  992.  
  993. /* HELP command
  994.  * Accepts: help flag
  995.  */
  996.  
  997. void c_help (short help)
  998. {
  999.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(cmdtab),"Command, ",NIL,NIL};
  1000.   static fdb hlpfdb = {_CMCFM,NIL,&cmdfdb,NIL,NIL,NIL,NIL};
  1001.   pval parseval;
  1002.   fdb *used;
  1003.   if (help) cmxprintf ("\
  1004. The HELP command gives short descriptions of the MS commands.\n");
  1005.   else {
  1006.     noise ("WITH");
  1007.                 /* parse a command */
  1008.     parse (&hlpfdb,&parseval,&used);
  1009.     if (used == &hlpfdb) cmxprintf ("\
  1010. MS is a distributed electronic mail client using the Interactive Mail\n\
  1011. Access Protocol described in RFC-1176.  Its user interface is for the most\n\
  1012. part a subset of MM.  Please refer to the MM documentation on your system\n\
  1013. for more information.\n");
  1014.     else {
  1015.       void *which = (void *) parseval._pvint;
  1016.       confirm ();
  1017.       do_help (which);        /* dispatch to appropriate command */
  1018.     }
  1019.   }
  1020. }
  1021.  
  1022. /* KEYWORD command
  1023.  * Accepts: help flag
  1024.  */
  1025.  
  1026. void c_keyword (short help)
  1027. {
  1028.   if (help) cmxprintf ("\
  1029. The KEYWORD command makes the specified messages have the specified\n\
  1030. keyword.\n");
  1031.   else {
  1032.     char tmp[TMPLEN];
  1033.     pval parseval;
  1034.     fdb *used;
  1035.     static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  1036.     if (stream) {
  1037.       noise ("NAME");
  1038.                 /* parse the keyword */
  1039.       parse (&flgfdb,&parseval,&used);
  1040.       strcpy (tmp,(char *) parseval._pvint);
  1041.       if (do_sequence (NIL)) mail_setflag (stream,sequence,tmp);
  1042.     }
  1043.     else cmerr ("No mailbox is currently open");
  1044.   }
  1045. }
  1046.  
  1047.  
  1048. /* KILL command
  1049.  * Accepts: help flag
  1050.  */
  1051.  
  1052. void c_kill (short help)
  1053. {
  1054.   int i;
  1055.   if (help) cmxprintf ("\
  1056. The KILL command deletes the current message and does an implicit NEXT.\n");
  1057.   else {
  1058.     if (!stream) cmerr ("No mailbox is currently open");
  1059.     else {
  1060.       if (!current) cmerr ("No current message");
  1061.       else {
  1062.     char tmp[TMPLEN];
  1063.     noise ("MESSAGE");
  1064.     confirm ();
  1065.                 /* delete the current message */
  1066.     sprintf (tmp,"%d",current);
  1067.     mail_setflag (stream,tmp,"\\Deleted");
  1068.     if (current >= nmsgs) cmxprintf ("%%No next message\n");
  1069.     else {            /* invalidate the current sequence */
  1070.       for (i=1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  1071.                 /* select next message and type it */
  1072.       mail_elt (stream,++current)->spare = T;
  1073.       sprintf (sequence,"%d",current);
  1074.       more (type_message,current);
  1075.     }
  1076.       }
  1077.     }
  1078.   }
  1079. }
  1080.  
  1081. /* LITERAL-TYPE command
  1082.  * Accepts: help flag
  1083.  */
  1084.  
  1085. void c_literal_type (short help)
  1086. {
  1087.   if (help) cmxprintf ("\
  1088. The LITERAL-TYPE command types the specified messages in original format.\n");
  1089.   else {
  1090.     int i;
  1091.     if (!do_sequence (NIL)) return;
  1092.                 /* type messages */
  1093.     for (i = 1; i <= nmsgs; ++i) if (mail_elt (stream,i)->spare)
  1094.       more (literal_type_message,i);
  1095.   }
  1096. }
  1097.  
  1098.  
  1099. /* MARK command
  1100.  * Accepts: help flag
  1101.  */
  1102.  
  1103. void c_mark (short help)
  1104. {
  1105.   if (help) cmxprintf ("\
  1106. The MARK command makes the specified messages be marked as seen.\n");
  1107.   else if (do_sequence (NIL)) mail_setflag (stream,sequence,"\\Seen");
  1108. }
  1109.  
  1110. /* MOVE command
  1111.  * Accepts: help flag
  1112.  */
  1113.  
  1114. void c_move (short help)
  1115. {
  1116.   if (help) cmxprintf ("\
  1117. The MOVE command moves the specified messages into the specified mailbox\n\
  1118. and then deletes them from this mailbox.\n");
  1119.   else {
  1120.     char tmp[TMPLEN];
  1121.     pval parseval;
  1122.     fdb *used;
  1123.     static brktab mbxbrk = {
  1124.       {                /* 1st char break array */
  1125.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1126.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  1127.       },
  1128.       {                /* subsequent char break array */
  1129.                 /* same as above, plus dots */
  1130.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1131.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  1132.       }
  1133.     };
  1134.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox on this server",
  1135.                "INBOX",&mbxbrk};
  1136.     if (stream) {
  1137.       noise ("TO MAILBOX");
  1138.                 /* parse the mailbox name */
  1139.       parse (&mbxfdb,&parseval,&used);
  1140.       strcpy (tmp,atmbuf);    /* note the mailbox name */
  1141.       if (do_sequence (NIL)) mail_move (stream,sequence,tmp);
  1142.     }
  1143.     else cmerr ("No mailbox is currently open");
  1144.   }
  1145. }
  1146.  
  1147. /* NEXT command
  1148.  * Accepts: help flag
  1149.  */
  1150.  
  1151. void c_next (short help)
  1152. {
  1153.   int i;
  1154.   if (help) cmxprintf ("\
  1155. The NEXT command types the next message in the mailbox.\n");
  1156.   else {
  1157.     if (!stream) cmerr ("No mailbox is currently open");
  1158.     else {
  1159.       if (!current) cmerr ("No current message");
  1160.       else {
  1161.     char tmp[TMPLEN];
  1162.     noise ("MESSAGE");
  1163.     confirm ();
  1164.     if (current >= nmsgs)
  1165.       cmxprintf (" Currently at end, message %d\n",current);
  1166.     else {
  1167.                 /* invalidate the current sequence */
  1168.       for (i=1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  1169.                 /* select next message */
  1170.       mail_elt (stream,++current)->spare = T;
  1171.       sprintf (sequence,"%d",current);
  1172.       more (type_message,current);
  1173.     }
  1174.       }
  1175.     }
  1176.   }
  1177. }
  1178.  
  1179. /* POST command
  1180.  * Accepts: help flag
  1181.  */
  1182.  
  1183. void c_post (short help)
  1184. {
  1185.   if (help) cmxprintf ("Compose and send a new BBoard message.\n");
  1186.   else {
  1187.     char tmp[TMPLEN];
  1188.     ENVELOPE *msg = NIL;
  1189.     BODY *body;
  1190.     pval parseval;
  1191.     fdb *used;
  1192.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1193.     static fdb optfdb = {_CMCFM,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1194.     static para_data pd = {NIL,NIL};
  1195.     static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  1196.     parafdb._cmdat = (pdat) &pd;
  1197.     noise ("TO BULLETIN BOARD(S)");
  1198.     linfdb._cmhlp = "list of bulletin boards";
  1199.     parse (&linfdb,&parseval,&used);
  1200.     if (atmbuf[0]) {        /* if specified recipient on command line */
  1201.       msg = send_init ();    /* get a message block and store to list */
  1202.       msg->newsgroups = cpystr (atmbuf);
  1203.     }
  1204.     confirm ();
  1205.                 /* get a message block now if not one yet */
  1206.     if (!msg) msg = send_init ();
  1207.     while (!msg->newsgroups) {    /* loop here until he gives a BBoard */
  1208.       prompt ("BBoard(s): ");    /* prompt for and parse BBoard-list */
  1209.       cmsetrp ();        /* set reparse trap */
  1210.       parse (&linfdb,&parseval,&used);
  1211.                 /* free old one in case reparse */
  1212.       if (msg->newsgroups) fs_give ((void **) &msg->newsgroups);
  1213.       msg->newsgroups = cpystr (atmbuf);
  1214.       confirm ();
  1215.     }
  1216.                 /* Subject is optional */
  1217.     linfdb._cmlst = (fdb *) &optfdb;
  1218.     prompt ("Subject: ");    /* prompt for and get Subject */
  1219.     cmsetrp ();            /* set reparse trap */
  1220.     linfdb._cmhlp = "single-line subject for this posting";
  1221.     parse (&linfdb,&parseval,&used);
  1222.                 /* free old one in case reparse */
  1223.     if (msg->subject) fs_give ((void **) &msg->subject);
  1224.     if (atmbuf[0]) msg->subject = cpystr (atmbuf);
  1225.     confirm ();
  1226.     if (body = send_text ()) {    /* get text and send message */
  1227.       send_level (msg,body);
  1228.       mail_free_body (&body);
  1229.     }
  1230.     mail_free_envelope (&msg);    /* flush the message */
  1231.   }
  1232. }
  1233.  
  1234. /* PREVIOUS command
  1235.  * Accepts: help flag
  1236.  */
  1237.  
  1238. void c_previous (short help)
  1239. {
  1240.   int i;
  1241.   if (help) cmxprintf ("\
  1242. The PREVIOUS command types the previous message in the mailbox.\n");
  1243.   else {
  1244.     char tmp[TMPLEN];
  1245.     if (!stream) cmerr ("No mailbox is currently open");
  1246.     else {
  1247.       if (!current) cmerr ("No current message");
  1248.       else {
  1249.     noise ("MESSAGE");
  1250.     confirm ();
  1251.     if (current == 1)
  1252.       cmxprintf (" Currently at beginning, message %d\n",current);
  1253.     else {
  1254.                 /* invalidate the current sequence */
  1255.       for (i=1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  1256.                 /* select previous message */
  1257.       mail_elt (stream,--current)->spare = T;
  1258.       sprintf (sequence,"%d",current);
  1259.       more (type_message,current);
  1260.     }
  1261.       }
  1262.     }
  1263.   }
  1264. }
  1265.  
  1266.  
  1267. /* QUIT command
  1268.  * Accepts: help flag
  1269.  */
  1270.  
  1271. void c_quit (short help)
  1272. {
  1273.   if (help) cmxprintf ("\
  1274. The QUIT command closes the mailbox and quits this program without\n\
  1275. expunging the mailbox.\n");
  1276.   else {
  1277.     noise ("PROGRAM");
  1278.     confirm ();
  1279.     done = T;            /* let top level know it's time to die */
  1280.   }
  1281. }
  1282.  
  1283. /* Read level command table */
  1284.  
  1285. static keywrd readcmds[] = {
  1286.   {"ANSWER",    KEY_INV,(keyval) r_answer},
  1287.   {"BLANK",    NIL,    (keyval) c_blank},
  1288.   {"BUG",    NIL,    (keyval) c_bug},
  1289.   {"COPY",    NIL,    (keyval) r_copy},
  1290.   {"D",        KEY_INV|KEY_ABR,(keyval) 7},
  1291.   {"DAYTIME",    NIL,    (keyval) c_daytime},
  1292.   {"DEBUG",    NIL,    (keyval) c_debug},
  1293.   {"DELETE",    NIL,    (keyval) r_delete},
  1294.   {"ECHO",    NIL,    (keyval) c_echo},
  1295.   {"FLAG",    NIL,    (keyval) r_flag},
  1296.   {"FORWARD",    NIL,    (keyval) r_forward},
  1297.   {"H",        KEY_INV|KEY_ABR,(keyval) 12},
  1298.   {"HEADER",    NIL,    (keyval) r_headers},
  1299.   {"HELP",    NIL,    (keyval) r_help},
  1300.   {"K",        KEY_INV|KEY_ABR,(keyval) 16},
  1301.   {"KEYWORD",    NIL,    (keyval) r_keyword},
  1302.   {"KILL",    NIL,    (keyval) r_kill},
  1303.   {"LITERAL-TYPE",NIL,    (keyval) r_literal_type},
  1304.   {"MARK",    NIL,    (keyval) r_mark},
  1305.   {"MOVE",    NIL,    (keyval) r_move},
  1306.   {"NEXT",    NIL,    (keyval) r_next},
  1307.   {"PIPE",    NIL,    (keyval) r_pipe},
  1308.   {"POST",    NIL,    (keyval) c_post},
  1309.   {"PREVIOUS",    NIL,    (keyval) r_previous},
  1310.   {"QUIT",    NIL,    (keyval) r_quit},
  1311.   {"R",        KEY_INV|KEY_ABR,(keyval) 27},
  1312.   {"REMAIL",    NIL,    (keyval) r_remail},
  1313.   {"REPLY",    NIL,    (keyval) r_answer},
  1314.   {"S",        KEY_INV|KEY_ABR,(keyval) 29},
  1315.   {"SEND",    NIL,    (keyval) c_send},
  1316.   {"STATUS",    NIL,    (keyval) r_status},
  1317.   {"TYPE",    NIL,    (keyval) r_type},
  1318.   {"UNANSWER",    NIL,    (keyval) r_unanswer},
  1319.   {"UNDELETE",    NIL,    (keyval) r_undelete},
  1320.   {"UNFLAG",    NIL,    (keyval) r_unflag},
  1321.   {"UNKEYWORD",    NIL,    (keyval) r_unkeyword},
  1322.   {"UNMARK",    NIL,    (keyval) r_unmark},
  1323.   {"VERSION",    NIL,    (keyval) c_version},
  1324. };
  1325. static keytab reatab = {(sizeof (readcmds)/sizeof (keywrd)),readcmds};
  1326.  
  1327. /* READ command
  1328.  * Accepts: help flag
  1329.  */
  1330.  
  1331. void c_read (short help)
  1332. {
  1333.   pval parseval;
  1334.   fdb *used;
  1335.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(reatab),"Command, ","NEXT",NIL};
  1336.   char *defseq = (stream && *stream->mailbox == '*') ? "NEW" : "UNSEEN";
  1337.   int olddone = done;
  1338.   if (help) cmxprintf ("\
  1339. The READ command enters read sub-mode on the specified messages.\n");
  1340.   else if (do_sequence (defseq)) for (current=1; current <= nmsgs; ++current)
  1341.     if (mail_elt (stream,current)->spare) {
  1342.                 /* make sure we have flags & envelope */
  1343.       mail_fetchstructure (stream,current,NIL);
  1344.       if (mail_elt (stream,current)->deleted)
  1345.     cmxprintf (" Message %d deleted\n",current);
  1346.       else {
  1347.     cmcls ();        /* zap the screen */
  1348.     more (type_message,current);
  1349.       }
  1350.       done = NIL;        /* not done yet */
  1351.       while (!done) {
  1352.     cmseter ();        /* set error trap */
  1353.                 /* exit on EOF */
  1354.     if (cmcsb._cmerr == CMxEOF) break;
  1355.     prompt ("MS-Read>");    /* prompt */
  1356.     cmsetrp ();        /* set reparse trap */
  1357.                 /* parse command */
  1358.     parse (&cmdfdb,&parseval,&used);
  1359.     do_cmd ((void *) parseval._pvint);
  1360.       }
  1361.       if (done > 0) break;    /* negative means NEXT command */
  1362.     }
  1363.   sequence[current = 0] = '\0';    /* invalidate sequence */
  1364.   done = olddone;        /* cancel done status */
  1365. }
  1366.  
  1367. /* REMAIL command
  1368.  * Accepts: help flag
  1369.  */
  1370.  
  1371. void c_remail (short help)
  1372. {
  1373.   if (help) cmxprintf ("Remail the specified messages to another mailbox.\n");
  1374.   else {
  1375.     char tmp[TMPLEN];
  1376.     int msgno;
  1377.     pval parseval;
  1378.     fdb *used;
  1379.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1380.     ADDRESS *adr = NIL;
  1381.     linfdb._cmhlp = "list of remail recipients in RFC 822 format";
  1382.     if (do_sequence (NIL)) {
  1383.       while (!adr) {
  1384.     cmseter ();        /* set error trap */
  1385.     prompt ("To: ");    /* get recipient list */
  1386.     cmsetrp ();        /* set reparse trap */
  1387.     parse (&linfdb,&parseval,&used);
  1388.                 /* parse recipient */
  1389.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  1390.     confirm ();
  1391.       }
  1392.       for (msgno = 1; msgno <= nmsgs; ++msgno)
  1393.     if (mail_elt (stream,msgno)->spare)
  1394.       remail_message ((current = msgno),adr);
  1395.       mail_free_address (&adr);    /* flush the address */
  1396.     }
  1397.   }
  1398. }
  1399.  
  1400. /* REMOVE command
  1401.  * Accepts: help flag
  1402.  */
  1403.  
  1404. void c_remove (short help)
  1405. {
  1406.   if (help) cmxprintf ("Removes an existing mailbox.\n");
  1407.   else {
  1408.     char tmp[TMPLEN];
  1409.     pval parseval;
  1410.     fdb *used;
  1411.     static brktab mbxbrk = {
  1412.       {                /* 1st char break array */
  1413.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1414.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1415.       },
  1416.       {                /* subsequent char break array */
  1417.                 /* same as above, plus dots */
  1418.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1419.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1420.       }
  1421.     };
  1422.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox",NIL,&mbxbrk};
  1423.     noise ("MAILBOX NAMED");
  1424.                 /* parse the mailbox name */
  1425.     parse (&mbxfdb,&parseval,&used);
  1426.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  1427.     confirm ();
  1428.     mail_delete (NIL,tmp);
  1429.   }
  1430. }
  1431.  
  1432. /* RENAME command
  1433.  * Accepts: help flag
  1434.  */
  1435.  
  1436. void c_rename (short help)
  1437. {
  1438.   if (help) cmxprintf ("Renames an existing mailbox.\n");
  1439.   else {
  1440.     char tmp[TMPLEN],tmpx[TMPLEN];
  1441.     pval parseval;
  1442.     fdb *used;
  1443.     static brktab mbxbrk = {
  1444.       {                /* 1st char break array */
  1445.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1446.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1447.       },
  1448.       {                /* subsequent char break array */
  1449.                 /* same as above, plus dots */
  1450.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1451.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1452.       }
  1453.     };
  1454.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox",NIL,&mbxbrk};
  1455.     noise ("MAILBOX NAMED");
  1456.                 /* parse the mailbox name */
  1457.     parse (&mbxfdb,&parseval,&used);
  1458.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  1459.     noise ("TO");
  1460.                 /* parse the mailbox name */
  1461.     parse (&mbxfdb,&parseval,&used);
  1462.     strcpy (tmpx,atmbuf);    /* note the mailbox name */
  1463.     confirm ();
  1464.     mail_rename (NIL,tmp,tmpx);
  1465.   }
  1466. }
  1467.  
  1468. /* SEND command
  1469.  * Accepts: help flag
  1470.  */
  1471.  
  1472. void c_send (short help)
  1473. {
  1474.   if (help) cmxprintf ("Compose and send a message.\n");
  1475.   else {
  1476.     char tmp[TMPLEN];
  1477.     ENVELOPE *msg = NIL;
  1478.     BODY *body;
  1479.     pval parseval;
  1480.     fdb *used;
  1481.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1482.     static fdb optfdb = {_CMCFM,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1483.     static para_data pd = {NIL,NIL};
  1484.     static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  1485.     parafdb._cmdat = (pdat) &pd;
  1486.     noise ("MESSAGE TO");
  1487.     linfdb._cmhlp = "list of primary recipients in RFC 822 format";
  1488.     parse (&linfdb,&parseval,&used);
  1489.     if (atmbuf[0]) {        /* if specified recipient on command line */
  1490.       msg = send_init ();    /* get a message block and store to list */
  1491.       rfc822_parse_adrlist (&msg->to,atmbuf,curhst);
  1492.     }
  1493.     confirm ();
  1494.                 /* get a message block now if not one yet */
  1495.     if (!msg) msg = send_init ();
  1496.     if (!msg->to) {        /* if no command line do hand-holding */
  1497.       while (!msg->to) {    /* loop here until he gives a To-list */
  1498.     prompt ("To: ");    /* prompt for and parse To-list */
  1499.     cmsetrp ();        /* set reparse trap */
  1500.     parse (&linfdb,&parseval,&used);
  1501.                 /* free old one in case reparse */
  1502.     if (msg->to) mail_free_address (&msg->to);
  1503.     rfc822_parse_adrlist (&msg->to,atmbuf,curhst);
  1504.     confirm ();
  1505.       }
  1506.                 /* cc and Subject are optional */
  1507.       linfdb._cmlst = (fdb *) &optfdb;
  1508.       prompt ("cc: ");        /* prompt for and parse cc-list */
  1509.       cmsetrp ();        /* set reparse trap */
  1510.       linfdb._cmhlp = "list of secondary recipients in RFC 822 format";
  1511.       parse (&linfdb,&parseval,&used);
  1512.                 /* free old one in case reparse */
  1513.       if (msg->cc) mail_free_address (&msg->cc);
  1514.       if (atmbuf[0]) rfc822_parse_adrlist (&msg->cc,atmbuf,curhst);
  1515.       confirm ();
  1516.     }
  1517.     prompt ("Subject: ");    /* prompt for and get Subject */
  1518.     cmsetrp ();            /* set reparse trap */
  1519.     linfdb._cmhlp = "single-line subject for this message";
  1520.     parse (&linfdb,&parseval,&used);
  1521.                 /* free old one in case reparse */
  1522.     if (msg->subject) fs_give ((void **) &msg->subject);
  1523.     if (atmbuf[0]) msg->subject = cpystr (atmbuf);
  1524.     confirm ();
  1525.     if (body = send_text ()) {    /* get text and send message */
  1526.       send_level (msg,body);
  1527.       mail_free_body (&body);
  1528.     }
  1529.     mail_free_envelope (&msg);    /* flush the message */
  1530.   }
  1531. }
  1532.  
  1533. /* STATUS command
  1534.  * Accepts: help flag
  1535.  */
  1536.  
  1537. void c_status (short help)
  1538. {
  1539.   if (help) cmxprintf ("Type status of current mailbox.\n");
  1540.   else {
  1541.     noise ("OF CURRENT MAILBOX");
  1542.     confirm ();
  1543.     if (stream) {
  1544.       do_status (stream);    /* output the status */
  1545.       if (sequence[0]) cmxprintf (" Current sequence: %s\n",sequence);
  1546.     }
  1547.     else cmxprintf ("%%No mailbox is currently open\n");
  1548.   }
  1549. }
  1550.  
  1551. static keywrd subcmds[] = {
  1552.   {"BBOARD",    NIL,    (keyval) NIL},
  1553.   {"MAILBOX",    NIL,    (keyval) T},
  1554. };
  1555. static keytab subtab = {(sizeof (subcmds)/sizeof (keywrd)),subcmds};
  1556.  
  1557.  
  1558. /* SUBSCRIBE command
  1559.  * Accepts: help flag
  1560.  */
  1561.  
  1562. void c_subscribe (short help)
  1563. {
  1564.   if (help) cmxprintf ("Subscribe to a mailbox or bboard.\n");
  1565.   else {
  1566.     int i;
  1567.     char tmp[TMPLEN];
  1568.     pval parseval;
  1569.     fdb *used;
  1570.     static brktab mbxbrk = {
  1571.       {                /* 1st char break array */
  1572.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1573.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1574.       },
  1575.       {                /* subsequent char break array */
  1576.                 /* same as above, plus dots */
  1577.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1578.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1579.       }
  1580.     };
  1581.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox",NIL,&mbxbrk};
  1582.     static fdb subfdb = {_CMKEY,NIL,NIL,(pdat) &(subtab),
  1583.                "subscription object, ","BBOARD",NIL};
  1584.     noise ("TO");
  1585.     parse (&subfdb,&parseval,&used);
  1586.     noise ((i = parseval._pvint) ? "MAILBOX NAMED" : "BBOARD NAMED");
  1587.                 /* parse the mailbox name */
  1588.     parse (&mbxfdb,&parseval,&used);
  1589.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  1590.     confirm ();
  1591.     if (i) mail_subscribe (NIL,tmp);
  1592.     else mail_subscribe_bboard (NIL,tmp);
  1593.   }
  1594. }
  1595.  
  1596. /* TAKE command
  1597.  * Accepts: help flag
  1598.  */
  1599.  
  1600. void c_take (short help)
  1601. {
  1602.   if (help) cmxprintf ("Take commands from a file.\n");
  1603.   else cmtake (takelevel);
  1604. }
  1605.  
  1606.  
  1607. /* Routine called by TAKE to invoke top level */
  1608.  
  1609. void takelevel ()
  1610. {
  1611.   toplevel (NIL,NIL);        /* invoke top level */
  1612. }
  1613.  
  1614.  
  1615. /* TYPE command
  1616.  * Accepts: help flag
  1617.  */
  1618.  
  1619. void c_type (short help)
  1620. {
  1621.   if (help) cmxprintf ("The TYPE command types the specified messages.\n");
  1622.   else {
  1623.     int i;
  1624.     if (!do_sequence (NIL)) return;
  1625.     for (i = 1; i <= nmsgs; ++i) if (mail_elt (stream,i)->spare)
  1626.       more (type_message,i);
  1627.   }
  1628. }
  1629.  
  1630. /* UNANSWER command
  1631.  * Accepts: help flag
  1632.  */
  1633.  
  1634. void c_unanswer (short help)
  1635. {
  1636.   if (help) cmxprintf ("\
  1637. The UNANSWER command makes the specified messages not be answered.\n");
  1638.   else if (do_sequence (NIL)) mail_clearflag (stream,sequence,"\\Answered");
  1639. }
  1640.  
  1641.  
  1642. /* UNDELETE command
  1643.  * Accepts: help flag
  1644.  */
  1645.  
  1646. void c_undelete (short help)
  1647. {
  1648.   if (help) cmxprintf ("\
  1649. The UNDELETE command makes the specified messages not be deleted.\n");
  1650.   else if (do_sequence (NIL)) mail_clearflag (stream,sequence,"\\Deleted");
  1651. }
  1652.  
  1653.  
  1654. /* UNFLAG command
  1655.  * Accepts: help flag
  1656.  */
  1657.  
  1658. void c_unflag (short help)
  1659. {
  1660.   if (help) cmxprintf ("\
  1661. The UNFLAG command makes the specified messages not be flagged as urgent.\n");
  1662.   else if (do_sequence (NIL)) mail_clearflag (stream,sequence,"\\Flagged");
  1663. }
  1664.  
  1665. /* UNKEYWORD command
  1666.  * Accepts: help flag
  1667.  */
  1668.  
  1669. void c_unkeyword (short help)
  1670. {
  1671.   if (help) cmxprintf ("\
  1672. The UNKEYWORD command makes the specified messages not have the specified\n\
  1673. keyword.\n");
  1674.   else {
  1675.     char tmp[TMPLEN];
  1676.     pval parseval;
  1677.     fdb *used;
  1678.     static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  1679.     if (stream) {
  1680.       noise ("NAME");
  1681.                 /* parse the keyword */
  1682.       parse (&flgfdb,&parseval,&used);
  1683.       strcpy (tmp,(char *) parseval._pvint);
  1684.       if (do_sequence (NIL)) mail_clearflag (stream,sequence,tmp);
  1685.     }
  1686.     else cmerr ("No mailbox is currently open");
  1687.   }
  1688. }
  1689.  
  1690.  
  1691. /* UNMARK command
  1692.  * Accepts: help flag
  1693.  */
  1694.  
  1695. void c_unmark (short help)
  1696. {
  1697.   if (help) cmxprintf ("\
  1698. The UNMARK command makes the specified messages not be marked as seen.\n");
  1699.   else if (do_sequence (NIL)) mail_clearflag (stream,sequence,"\\Seen");
  1700. }
  1701.  
  1702. /* UNSUBSCRIBE command
  1703.  * Accepts: help flag
  1704.  */
  1705.  
  1706. void c_unsubscribe (short help)
  1707. {
  1708.   if (help) cmxprintf ("Subscribe to a mailbox or bboard.\n");
  1709.   else {
  1710.     int i;
  1711.     char tmp[TMPLEN];
  1712.     pval parseval;
  1713.     fdb *used;
  1714.     static brktab mbxbrk = {
  1715.       {                /* 1st char break array */
  1716.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1717.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1718.       },
  1719.       {                /* subsequent char break array */
  1720.                 /* same as above, plus dots */
  1721.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1722.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1723.       }
  1724.     };
  1725.     static fdb mb2fdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox name",NIL,&mbxbrk};
  1726.     static fdb mbxfdb = {_CMKEY,NIL,&mb2fdb,(pdat)& (mbxtab),"known mailbox, ",
  1727.              NIL,&mbxbrk};
  1728.     static fdb bb2fdb = {_CMFLD,CM_SDH,NIL,NIL,"bboard name",NIL,&mbxbrk};
  1729.     static fdb bbdfdb = {_CMKEY,NIL,&bb2fdb,(pdat)& (bbdtab),"known bboard, ",
  1730.              NIL,&mbxbrk};
  1731.     static fdb subfdb = {_CMKEY,NIL,NIL,(pdat) &(subtab),
  1732.                "subscription object, ","BBOARD",NIL};
  1733.     noise ("FROM");
  1734.     parse (&subfdb,&parseval,&used);
  1735.     noise ((i = parseval._pvint) ? "MAILBOX NAMED" : "BBOARD NAMED");
  1736.                 /* parse the mailbox name */
  1737.     parse (i ? &mbxfdb : &bbdfdb,&parseval,&used);
  1738.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  1739.     confirm ();
  1740.     if (i) mail_unsubscribe (NIL,tmp);
  1741.     else mail_unsubscribe_bboard (NIL,tmp);
  1742.   }
  1743. }
  1744.  
  1745.  
  1746. /* VERSION command
  1747.  * Accepts: help flag
  1748.  */
  1749.  
  1750. void c_version (short help)
  1751. {
  1752.   if (help) cmxprintf ("Display the current version of this program.\n");
  1753.   else {
  1754.     noise ("OF MS");
  1755.     confirm ();
  1756.     do_version ();
  1757.     cmxprintf (" Written by %s\n%s\n",author,copyright);
  1758.   }
  1759. }
  1760.  
  1761. /* Read command level */
  1762.  
  1763.  
  1764. /* ANSWER command
  1765.  * Accepts: help flag
  1766.  */
  1767.  
  1768. void r_answer (short help)
  1769. {
  1770.   if (help) cmxprintf ("\
  1771. The REPLY command composes and sends an answer to this message.\n");
  1772.   else {
  1773.     int i;
  1774.     pval parseval;
  1775.     fdb *used;
  1776.     static fdb ansfdb = {_CMKEY,NIL,NIL,(pdat) &(anstab),"Answer option, ",
  1777.                "SENDER-ONLY",NIL};
  1778.     noise ("TO");        /* get reply option */
  1779.     parse (&ansfdb,&parseval,&used);
  1780.     i = parseval._pvint;    /* save user's selection */
  1781.     confirm ();
  1782.     answer_message (current,i);    /* do the answer and mark the message */
  1783.   }
  1784. }
  1785.  
  1786. /* COPY command
  1787.  * Accepts: help flag
  1788.  */
  1789.  
  1790. void r_copy (short help)
  1791. {
  1792.   if (help) cmxprintf ("\
  1793. The COPY command copies this message into the specified mailbox.\n");
  1794.   else {
  1795.     char tmp[TMPLEN];
  1796.     char copybox[TMPLEN];
  1797.     pval parseval;
  1798.     fdb *used;
  1799.     static brktab mbxbrk = {
  1800.       {                /* 1st char break array */
  1801.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1802.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  1803.       },
  1804.       {                /* subsequent char break array */
  1805.                 /* same as above, plus dots */
  1806.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1807.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  1808.       }
  1809.     };
  1810.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox on this server",
  1811.                "INBOX",&mbxbrk};
  1812.     noise ("TO MAILBOX");
  1813.                 /* parse the mailbox name */
  1814.     parse (&mbxfdb,&parseval,&used);
  1815.     strcpy (copybox,atmbuf);    /* note the mailbox name */
  1816.     confirm ();
  1817.     sprintf (tmp,"%d",current);
  1818.     mail_copy (stream,tmp,copybox);
  1819.   }
  1820. }
  1821.  
  1822. /* DELETE command
  1823.  * Accepts: help flag
  1824.  */
  1825.  
  1826. void r_delete (short help)
  1827. {
  1828.   if (help) cmxprintf ("\
  1829. The DELETE command makes this message be deleted (marked for\n\
  1830. removal by a subsequent EXIT or EXPUNGE command).\n");
  1831.   else {
  1832.     char tmp[TMPLEN];
  1833.     noise ("MESSAGE");
  1834.     confirm ();
  1835.     sprintf (tmp,"%d",current);
  1836.     mail_setflag (stream,tmp,"\\Deleted");
  1837.   }
  1838. }
  1839.  
  1840.  
  1841. /* FLAG command
  1842.  * Accepts: help flag
  1843.  */
  1844.  
  1845. void r_flag (short help)
  1846. {
  1847.   if (help) cmxprintf ("\
  1848. The FLAG command makes this message be flagged as urgent.\n");
  1849.   else {
  1850.     char tmp[TMPLEN];
  1851.     noise ("MESSAGE");
  1852.     confirm ();
  1853.     sprintf (tmp,"%d",current);
  1854.     mail_setflag (stream,tmp,"\\Flagged");
  1855.   }
  1856. }
  1857.  
  1858. /* FORWARD command
  1859.  * Accepts: help flag
  1860.  */
  1861.  
  1862. void r_forward (short help)
  1863. {
  1864.   if (help) cmxprintf ("\
  1865. Forwards this message with optional comments to another mailbox.\n");
  1866.   else {
  1867.     pval parseval;
  1868.     fdb *used;
  1869.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1870.     char tmp[TMPLEN];
  1871.     int i,j,k;
  1872.     char *hdr,*text;
  1873.     char *s;
  1874.     ENVELOPE *msg;
  1875.     BODY *body;
  1876.     ADDRESS *adr = NIL;
  1877.     noise ("MESSAGE TO");
  1878.     linfdb._cmhlp = "list of forward recipients in RFC 822 format";
  1879.     parse (&linfdb,&parseval,&used);
  1880.                 /* parse recipient */
  1881.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  1882.     if (!adr) {
  1883.       cmerr ("No forward recipient specified");
  1884.       return;
  1885.     }
  1886.     confirm ();
  1887.     msg = send_init ();        /* get message block */
  1888.     msg->to = adr;        /* set to-list */
  1889.     tmp[0] = '[';        /* build string */
  1890.                 /* get short from sans trailing spaces */
  1891.     mail_fetchfrom (tmp+1,stream,current,FROMLEN);
  1892.     for (s = tmp+FROMLEN; *s == ' '; --s) *s = '\0';
  1893.     *++s = ':'; *++s = ' ';
  1894.     strcpy (++s,(mail_fetchstructure (stream,current,NIL))->subject);
  1895.     msg->subject = cpystr (tmp);/* set up subject */
  1896.                 /* get header of message */
  1897.     mail_parameters (NIL,SET_PREFETCH,(void *) T);
  1898.     hdr = cpystr (mail_fetchheader (stream,current));
  1899.     mail_parameters (NIL,SET_PREFETCH,NIL);
  1900.                 /* get body of message */
  1901.     text = mail_fetchtext (stream,current);
  1902.     if (body = send_text ()) {    /* get initial text of comments */
  1903.                 /* resize the forward text */
  1904.       fs_resize ((void **) &body->contents.text,
  1905.          (i = strlen ((char *) body->contents.text)) +
  1906.          strlen (fwdhdr) + strlen (hdr) + strlen (text));
  1907.       sprintf ((char *) body->contents.text + i,"%s%s%s",fwdhdr,hdr,text);
  1908.       send_level (msg,body);    /* enter send-level */
  1909.       mail_free_body (&body);
  1910.     }
  1911.     fs_give ((void **) &hdr);    /* free header */
  1912.     mail_free_envelope (&msg);    /* flush the message */
  1913.   }
  1914. }
  1915.  
  1916. /* HEADER command
  1917.  * Accepts: help flag
  1918.  */
  1919.  
  1920. void r_headers (short help)
  1921. {
  1922.   if (help) cmxprintf ("\
  1923. The HEADERS command displays one-line summaries of this message.\n");
  1924.   else {
  1925.     noise ("OF CURRENT MESSAGE");
  1926.     confirm ();
  1927.     header_message (stdout,current);
  1928.   }
  1929. }
  1930.  
  1931.  
  1932. /* HELP command
  1933.  * Accepts: help flag
  1934.  */
  1935.  
  1936. void r_help (short help)
  1937. {
  1938.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(reatab),"Command, ",NIL,NIL};
  1939.   static fdb hlpfdb = {_CMCFM,NIL,&cmdfdb,NIL,NIL,NIL,NIL};
  1940.   pval parseval;
  1941.   fdb *used;
  1942.   if (help) cmxprintf ("\
  1943. The HELP command gives short descriptions of the MS read level commands.\n");
  1944.   else {
  1945.     noise ("WITH");
  1946.                 /* parse a command */
  1947.     parse (&hlpfdb,&parseval,&used);
  1948.     if (used == &hlpfdb) cmxprintf ("\
  1949. MS is at read level, in which commands apply only to the current message.\n");
  1950.     else {
  1951.       void *which = (void *) parseval._pvint;
  1952.       confirm ();
  1953.       do_help (which);        /* dispatch to appropriate command */
  1954.     }
  1955.   }
  1956. }
  1957.  
  1958. /* KEYWORD command
  1959.  * Accepts: help flag
  1960.  */
  1961.  
  1962. void r_keyword (short help)
  1963. {
  1964.   if (help) cmxprintf ("\
  1965. The KEYWORD command makes this message have the specified keyword.\n");
  1966.   else {
  1967.     char key[TMPLEN];
  1968.     char tmp[TMPLEN];
  1969.     pval parseval;
  1970.     fdb *used;
  1971.     static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  1972.     noise ("NAME");
  1973.                 /* parse the keyword */
  1974.     parse (&flgfdb,&parseval,&used);
  1975.     strcpy (key,(char *) parseval._pvint);
  1976.     confirm ();
  1977.     sprintf (tmp,"%d",current);
  1978.     mail_setflag (stream,tmp,key);
  1979.   }
  1980. }
  1981.  
  1982.  
  1983. /* KILL command
  1984.  * Accepts: help flag
  1985.  */
  1986.  
  1987. void r_kill (short help)
  1988. {
  1989.   if (help) cmxprintf ("\
  1990. The KILL command deletes the current message and does an implicit NEXT.\n");
  1991.   else {
  1992.     char tmp[TMPLEN];
  1993.     noise ("MESSAGE");
  1994.     confirm ();
  1995.     sprintf (tmp,"%d",current);    /* delete the current message */
  1996.     mail_setflag (stream,tmp,"\\Deleted");
  1997.     done = -1;            /* exit this message */
  1998.   }
  1999. }
  2000.  
  2001. /* LITERAL-TYPE command
  2002.  * Accepts: help flag
  2003.  */
  2004.  
  2005. void r_literal_type (short help)
  2006. {
  2007.   if (help) cmxprintf ("\
  2008. The LITERAL-TYPE command types this message in original form.\n");
  2009.   else {
  2010.     noise ("MESSAGE");
  2011.     confirm ();
  2012.     if (stream) {
  2013.                 /* type the message */
  2014.       if (current) more (literal_type_message,current);
  2015.       else cmxprintf ("%%No current message\n");
  2016.     }
  2017.     else cmxprintf ("%%No mailbox is currently open\n");
  2018.   }
  2019. }
  2020.  
  2021.  
  2022. /* MARK command
  2023.  * Accepts: help flag
  2024.  */
  2025.  
  2026. void r_mark (short help)
  2027. {
  2028.   if (help) cmxprintf ("\
  2029. The MARK command makes this message be marked as seen.\n");
  2030.   else {
  2031.     char tmp[TMPLEN];
  2032.     noise ("MESSAGE");
  2033.     confirm ();
  2034.     sprintf (tmp,"%d",current);
  2035.     mail_setflag (stream,tmp,"\\Seen");
  2036.   }
  2037. }
  2038.  
  2039. /* MOVE command
  2040.  * Accepts: help flag
  2041.  */
  2042.  
  2043. void r_move (short help)
  2044. {
  2045.   if (help) cmxprintf ("\
  2046. The MOVE command moves this message into the specified mailbox\n\
  2047. and then deletes them from this mailbox.\n");
  2048.   else {
  2049.     char tmp[TMPLEN];
  2050.     char copybox[TMPLEN];
  2051.     pval parseval;
  2052.     fdb *used;
  2053.     static brktab mbxbrk = {
  2054.       {                /* 1st char break array */
  2055.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  2056.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  2057.       },
  2058.       {                /* subsequent char break array */
  2059.                 /* same as above, plus dots */
  2060.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  2061.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  2062.       }
  2063.     };
  2064.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox on this server",
  2065.                "INBOX",&mbxbrk};
  2066.     noise ("TO MAILBOX");
  2067.                 /* parse the mailbox name */
  2068.     parse (&mbxfdb,&parseval,&used);
  2069.     strcpy (copybox,atmbuf);    /* note the mailbox name */
  2070.     confirm ();
  2071.     sprintf (tmp,"%d",current);
  2072.     mail_move (stream,tmp,copybox);
  2073.   }
  2074. }
  2075.  
  2076. /* NEXT command
  2077.  * Accepts: help flag
  2078.  */
  2079.  
  2080. void r_next (short help)
  2081. {
  2082.   if (help) cmxprintf ("\
  2083. The NEXT command goes to the next message in the sequence.\n");
  2084.   else {
  2085.     noise ("MESSAGE");
  2086.     confirm ();
  2087.     done = -1;            /* let read level know it's time to next */
  2088.   }
  2089. }
  2090.  
  2091. /* PIPE command
  2092.  * Accepts: help flag
  2093.  */
  2094.  
  2095. void r_pipe (short help)
  2096. {
  2097.   if (help) cmxprintf ("Pipe to a program.\n");
  2098.   else {
  2099.     char tmp[TMPLEN];
  2100.     pval parseval;
  2101.     fdb *used;
  2102.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  2103.     static fdb optfdb = {_CMCFM,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  2104.     static para_data pd = {NIL,NIL};
  2105.     static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  2106.     parafdb._cmdat = (pdat) &pd;
  2107.     noise ("TO PROGRAM");
  2108.     linfdb._cmhlp = "program";
  2109.     linfdb._cmdef = "more";
  2110.     parse (&linfdb,&parseval,&used);
  2111.     strcpy (tmp,atmbuf);
  2112.     confirm ();
  2113.     if (stream) {        /* type the message */
  2114.       if (current) {
  2115. #if unix
  2116.     FILE *pipe = popen (tmp,"w");
  2117.     if (pipe) {
  2118.       fflush (stdout);    /* make sure regular output is done */
  2119.       fflush (stderr);
  2120.       critical = T;        /* ignore CTRL/C while running */
  2121.       literal_type_message (pipe,current);
  2122.       fflush (pipe);    /* wait for output to be done */
  2123.       pclose (pipe);    /* close the pipe */
  2124.       critical = NIL;    /* allow CTRL/C again */
  2125.     }
  2126. #endif
  2127.       }
  2128.       else cmxprintf ("%%No current message\n");
  2129.     }
  2130.     else cmxprintf ("%%No mailbox is currently open\n");
  2131.   }
  2132. }
  2133.  
  2134. /* PREVIOUS command
  2135.  * Accepts: help flag
  2136.  */
  2137.  
  2138. void r_previous (short help)
  2139. {
  2140.   if (help) cmxprintf ("\
  2141. The PREVIOUS command types the previous message in the mailbox.\n");
  2142.   else {
  2143.     int i;
  2144.     noise ("MESSAGE");
  2145.     confirm ();
  2146.                 /* look for earlier current message */
  2147.     for (i = current-1; i >= 1; --i) if (mail_elt (stream,i)->spare) {
  2148.       current = i;        /* this is the new current message */
  2149.       if (mail_elt (stream,current)->deleted)
  2150.     cmxprintf (" Message %d deleted\n",current);
  2151.       else {
  2152.     cmcls ();        /* zap the screen */
  2153.     more (type_message,current);
  2154.       }
  2155.       return;            /* skip error message */
  2156.     }
  2157.     cmxprintf (" Currently at beginning, message %d\n",current);
  2158.   }
  2159. }
  2160.  
  2161. /* QUIT command
  2162.  * Accepts: help flag
  2163.  */
  2164.  
  2165. void r_quit (short help)
  2166. {
  2167.   if (help) cmxprintf ("\
  2168. The QUIT command exits read level, returning to top level.\n");
  2169.   else {
  2170.     noise ("READ LEVEL");
  2171.     confirm ();
  2172.     done = T;            /* let read level know it's time to die */
  2173.   }
  2174. }
  2175.  
  2176.  
  2177. /* REMAIL command
  2178.  * Accepts: help flag
  2179.  */
  2180.  
  2181. void r_remail (short help)
  2182. {
  2183.   if (help) cmxprintf ("Remail this message to another mailbox.\n");
  2184.   else {
  2185.     pval parseval;
  2186.     fdb *used;
  2187.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  2188.     char *text;
  2189.     ENVELOPE *msg;
  2190.     ADDRESS *adr = NIL;
  2191.     noise ("MESSAGE TO");
  2192.     linfdb._cmhlp = "list of remail recipients in RFC 822 format";
  2193.     parse (&linfdb,&parseval,&used);
  2194.                 /* parse recipient */
  2195.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  2196.     if (!adr) {
  2197.       cmerr ("No remail recipient specified");
  2198.       return;
  2199.     }
  2200.     confirm ();
  2201.     remail_message (current,adr);
  2202.     mail_free_address (&adr);    /* flush the address */
  2203.   }
  2204. }
  2205.  
  2206. /* STATUS command
  2207.  * Accepts: help flag
  2208.  */
  2209.  
  2210. void r_status (short help)
  2211. {
  2212.   if (help) cmxprintf ("Type status of current mailbox.\n");
  2213.   else {
  2214.     noise ("OF CURRENT MAILBOX");
  2215.     confirm ();
  2216.     if (stream) {
  2217.       do_status (stream);    /* output the status */
  2218.       if (current) cmxprintf (" Currently at message %d\n",current);
  2219.     }
  2220.     else cmxprintf ("%%No mailbox is currently open\n");
  2221.   }
  2222. }
  2223.  
  2224.  
  2225. /* TYPE command
  2226.  * Accepts: help flag
  2227.  */
  2228.  
  2229. void r_type (short help)
  2230. {
  2231.   if (help) cmxprintf ("The TYPE command types this message.\n");
  2232.   else {
  2233.     noise ("MESSAGE");
  2234.     confirm ();
  2235.     if (stream) {
  2236.                 /* type the message */
  2237.       if (current) more (type_message,current);
  2238.       else cmxprintf ("%%No current message\n");
  2239.     }
  2240.     else cmxprintf ("%%No mailbox is currently open\n");
  2241.   }
  2242. }
  2243.  
  2244. /* UNANSWER command
  2245.  * Accepts: help flag
  2246.  */
  2247.  
  2248. void r_unanswer (short help)
  2249. {
  2250.   if (help) cmxprintf ("\
  2251. The UNANSWER command makes this message not be answered.\n");
  2252.   else {
  2253.     char tmp[TMPLEN];
  2254.     noise ("MESSAGE");
  2255.     confirm ();
  2256.     sprintf (tmp,"%d",current);
  2257.     mail_clearflag (stream,tmp,"\\Answered");
  2258.   }
  2259. }
  2260.  
  2261.  
  2262. /* UNDELETE command
  2263.  * Accepts: help flag
  2264.  */
  2265.  
  2266. void r_undelete (short help)
  2267. {
  2268.   if (help) cmxprintf ("\
  2269. The UNDELETE command makes this message not be deleted.\n");
  2270.   else {
  2271.     char tmp[TMPLEN];
  2272.     noise ("MESSAGE");
  2273.     confirm ();
  2274.     sprintf (tmp,"%d",current);
  2275.     mail_clearflag (stream,tmp,"\\Deleted");
  2276.   }
  2277. }
  2278.  
  2279.  
  2280. /* UNFLAG command
  2281.  * Accepts: help flag
  2282.  */
  2283.  
  2284. void r_unflag (short help)
  2285. {
  2286.   if (help) cmxprintf ("\
  2287. The UNFLAG command makes this message not be flagged as urgent.\n");
  2288.   else {
  2289.     char tmp[TMPLEN];
  2290.     noise ("MESSAGE");
  2291.     confirm ();
  2292.     sprintf (tmp,"%d",current);
  2293.     mail_clearflag (stream,tmp,"\\Flagged");
  2294.   }
  2295. }
  2296.  
  2297. /* UNKEYWORD command
  2298.  * Accepts: help flag
  2299.  */
  2300.  
  2301. void r_unkeyword (short help)
  2302. {
  2303.   if (help) cmxprintf ("\
  2304. The UNKEYWORD command makes this message not have the specified keyword.\n");
  2305.   else {
  2306.     char key[TMPLEN];
  2307.     char tmp[TMPLEN];
  2308.     pval parseval;
  2309.     fdb *used;
  2310.     static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  2311.     noise ("NAME");
  2312.                 /* parse the keyword */
  2313.     parse (&flgfdb,&parseval,&used);
  2314.     strcpy (key,(char *) parseval._pvint);
  2315.     confirm ();
  2316.     sprintf (tmp,"%d",current);
  2317.     mail_clearflag (stream,tmp,key);
  2318.   }
  2319. }
  2320.  
  2321.  
  2322. /* UNMARK command
  2323.  * Accepts: help flag
  2324.  */
  2325.  
  2326. void r_unmark (short help)
  2327. {
  2328.   if (help) cmxprintf ("\
  2329. The UNMARK command makes this message not be marked as seen.\n");
  2330.   else {
  2331.     char tmp[TMPLEN];
  2332.     noise ("MESSAGE");
  2333.     confirm ();
  2334.     sprintf (tmp,"%d",current);
  2335.     mail_clearflag (stream,tmp,"\\Seen");
  2336.   }
  2337. }
  2338.  
  2339. /* Send command level */
  2340.  
  2341.  
  2342. /* Send level command table */
  2343.  
  2344. static keywrd sendcmds[] = {
  2345.   {"BBOARDS",    NIL,    (keyval) s_bboards},
  2346.   {"BCC",    NIL,    (keyval) s_bcc},
  2347.   {"BLANK",    NIL,    (keyval) c_blank},
  2348.   {"CC",    NIL,    (keyval) s_cc},
  2349.   {"D",        KEY_INV|KEY_ABR,(keyval) 7},
  2350.   {"DAYTIME",    NIL,    (keyval) c_daytime},
  2351.   {"DEBUG",    NIL,    (keyval) c_debug},
  2352.   {"DISPLAY",    NIL,    (keyval) s_display},
  2353.   {"ECHO",    NIL,    (keyval) c_echo},
  2354.   {"ERASE",    NIL,    (keyval) s_erase},
  2355.   {"HELP",    NIL,    (keyval) s_help},
  2356.   {"LITERAL-TYPE",NIL,    (keyval) r_literal_type},
  2357.   {"QUIT",    NIL,    (keyval) s_quit},
  2358.   {"REMOVE",    NIL,    (keyval) s_remove},
  2359.   {"SEND",    NIL,    (keyval) s_send},
  2360.   {"STATUS",    NIL,    (keyval) r_status},
  2361.   {"SUBJECT",    NIL,    (keyval) s_subject},
  2362.   {"TO",    NIL,    (keyval) s_to},
  2363.   {"TYPE",    NIL,    (keyval) r_type},
  2364. };
  2365. static keytab sndtab = {(sizeof (sendcmds)/sizeof (keywrd)),sendcmds};
  2366.  
  2367.  
  2368. /* Send command level
  2369.  * Accepts: message
  2370.  */
  2371.  
  2372. void send_level (ENVELOPE *msg,BODY *body)
  2373. {
  2374.   pval parseval;
  2375.   fdb *used;
  2376.   static fdb sndfdb = {_CMKEY,NIL,NIL,(pdat) &(sndtab),"Command, ","SEND",NIL};
  2377.   int olddone = done;        /* hold calling done */
  2378.   done = NIL;            /* not done in send yet */
  2379.   while (!done) {        /* loop until done */
  2380.     cmseter ();            /* set error trap */
  2381.                 /* exit on EOF */
  2382.     if (cmcsb._cmerr == CMxEOF) break;
  2383.     prompt ("MS-Send>");    /* prompt */
  2384.     cmsetrp ();            /* set reparse trap */
  2385.                 /* parse command */
  2386.     parse (&sndfdb,&parseval,&used);
  2387.                 /* do the command */
  2388.     do_scmd ((void *) parseval._pvint,msg,body);
  2389.   }
  2390.   done = olddone;        /* so we don't bust out of top level */
  2391. }
  2392.  
  2393. /* Execute command
  2394.  * Accepts: function
  2395.  *        message
  2396.  */
  2397.  
  2398. void do_scmd (int (*f)(short help,ENVELOPE *msg,BODY *body),
  2399.           ENVELOPE *msg,BODY *body)
  2400. {
  2401.   (*f)((short) NIL,msg,body);    /* call function with help flag off */
  2402. }
  2403.  
  2404.  
  2405. /* Execute help for command
  2406.  * Accepts: function
  2407.  *        message
  2408.  */
  2409.  
  2410. void do_shelp (int (*f)(short help,ENVELOPE *msg,BODY *body),
  2411.            ENVELOPE *msg,BODY *body)
  2412. {
  2413.   (*f)((short) T,msg,body);    /* call function with help flag on */
  2414. }
  2415.  
  2416. /* Send command execution routines */
  2417.  
  2418.  
  2419. /* BBOARDS command
  2420.  * Accepts: help flag
  2421.  *        message
  2422.  */
  2423.  
  2424. void s_bboards (short help,ENVELOPE *msg,BODY *body)
  2425. {
  2426.   if (help) cmxprintf ("\
  2427. The BBOARDS command sets a new bulletin boards list.\n");
  2428.   else {
  2429.     char newsgroups[TMPLEN];
  2430.     pval parseval;
  2431.     fdb *used;
  2432.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"list of BBoards",NIL,NIL};
  2433.                 /* get newsgroups */
  2434.     parse (&linfdb,&parseval,&used);
  2435.     strcpy (newsgroups,atmbuf);    /* copy newsgroups into temp buffer */
  2436.     confirm ();
  2437.                 /* flush the old newsgroups */
  2438.     if (msg->newsgroups) fs_give ((void **) &msg->newsgroups);
  2439.                 /* set new newsgroups */
  2440.     msg->newsgroups = newsgroups[0] ? cpystr (newsgroups) : NIL;
  2441.   }
  2442. }
  2443.  
  2444. /* BCC command
  2445.  * Accepts: help flag
  2446.  *        message
  2447.  */
  2448.  
  2449. void s_bcc (short help,ENVELOPE *msg,BODY *body)
  2450. {
  2451.   if (help) cmxprintf ("\
  2452. The BCC command adds recipients to the blind carbon copy (bcc) list.\n");
  2453.   else {
  2454.     ADDRESS *adr = NIL;
  2455.     ADDRESS *lst;
  2456.     pval parseval;
  2457.     fdb *used;
  2458.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"blind carbon copy list",
  2459.                NIL,NIL};
  2460.     noise ("TO");
  2461.     parse (&linfdb,&parseval,&used);
  2462.                 /* free old one in case reparse */
  2463.     if (adr) mail_free_address (&adr);
  2464.                 /* parse the address list */
  2465.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  2466.     confirm ();
  2467.     if (lst = msg->bcc) {    /* if a bcc list already */
  2468.                 /* run down the list until the end */
  2469.       while (lst->next) lst = lst->next;
  2470.       lst->next = adr;        /* and link at the end of the list */
  2471.     }
  2472.     else msg->bcc = adr;    /* else this is the bcc list */
  2473.   }
  2474. }
  2475.  
  2476. /* CC command
  2477.  * Accepts: help flag
  2478.  *        message
  2479.  */
  2480.  
  2481. void s_cc (short help,ENVELOPE *msg,BODY *body)
  2482. {
  2483.   if (help) cmxprintf ("\
  2484. The CC command adds recipients to the carbon copy (cc) list.\n");
  2485.   else {
  2486.     ADDRESS *adr = NIL;
  2487.     ADDRESS *lst;
  2488.     pval parseval;
  2489.     fdb *used;
  2490.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"carbon copy list",
  2491.                NIL,NIL};
  2492.     noise ("TO");
  2493.     parse (&linfdb,&parseval,&used);
  2494.                 /* free old one in case reparse */
  2495.     if (adr) mail_free_address (&adr);
  2496.                 /* parse the address list */
  2497.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  2498.     confirm ();
  2499.     if (lst = msg->cc) {    /* if a cc list already */
  2500.                 /* run down the list until the end */
  2501.       while (lst->next) lst = lst->next;
  2502.       lst->next = adr;        /* and link at the end of the list */
  2503.     }
  2504.     else msg->cc = adr;        /* else this is the cc list */
  2505.   }
  2506. }
  2507.  
  2508.  
  2509. /* DISPLAY command
  2510.  * Accepts: help flag
  2511.  *        message
  2512.  */
  2513.  
  2514. void s_display (short help,ENVELOPE *msg,BODY *body)
  2515. {
  2516.   void *message[2];
  2517.   if (help) cmxprintf ("\
  2518. The DISPLAY command displays the header and text of the message.\n");
  2519.   else {
  2520.     noise ("MESSAGE");
  2521.     confirm ();
  2522.     message[0] = (void *) msg;
  2523.     message[1] = (void *) body;
  2524.     more (do_display,(long) message);
  2525.   }
  2526. }
  2527.  
  2528. /* ERASE command
  2529.  * Accepts: help flag
  2530.  *        message
  2531.  */
  2532.  
  2533. #define ERBCC 0
  2534. #define ERCC 1
  2535. #define ERRNEWS 2
  2536. #define ERTO 3
  2537.  
  2538. static keywrd eracmds[] = {
  2539.   {"BBOARDS",    NIL,    (keyval) ERRNEWS},
  2540.   {"BCC",    NIL,    (keyval) ERBCC},
  2541.   {"CC",    NIL,    (keyval) ERCC},
  2542.   {"TO",    NIL,    (keyval) ERTO},
  2543. };
  2544. static keytab eratab = {(sizeof (eracmds)/sizeof (keywrd)),eracmds};
  2545.  
  2546.  
  2547. void s_erase (short help,ENVELOPE *msg,BODY *body)
  2548. {
  2549.   if (help) cmxprintf ("\
  2550. The ERASE command erases the specified recipient list.\n");
  2551.   else {
  2552.     int i;
  2553.     pval parseval;
  2554.     fdb *used;
  2555.     static fdb erafdb = {_CMKEY,NIL,NIL,(pdat) &(eratab),"Address list, ",
  2556.                NIL,NIL};
  2557.     noise ("LIST");
  2558.     parse (&erafdb,&parseval,&used);
  2559.     i = parseval._pvint;    /* save user's selection */
  2560.     confirm ();
  2561.     switch (i) {        /* now zap the appropriate list */
  2562.     case ERBCC:            /* bcc list */
  2563.       if (msg->bcc) mail_free_address (&msg->bcc);
  2564.       break;
  2565.     case ERCC:            /* cc list */
  2566.       if (msg->cc) mail_free_address (&msg->cc);
  2567.       break;
  2568.     case ERRNEWS:
  2569.       if (msg->newsgroups) fs_give ((void **) &msg->newsgroups);
  2570.       break;
  2571.     case ERTO:            /* to list */
  2572.     default:
  2573.       if (msg->to) mail_free_address (&msg->to);
  2574.       break;
  2575.     }
  2576.   }
  2577. }
  2578.  
  2579. /* HELP command
  2580.  * Accepts: help flag
  2581.  *        message
  2582.  */
  2583.  
  2584. void s_help (short help,ENVELOPE *msg,BODY *body)
  2585. {
  2586.   static fdb sndfdb = {_CMKEY,NIL,NIL,(pdat) &(sndtab),"Command, ",NIL,NIL};
  2587.   static fdb hlpfdb = {_CMCFM,NIL,&sndfdb,NIL,NIL,NIL,NIL};
  2588.   pval parseval;
  2589.   fdb *used;
  2590.   if (help) cmxprintf ("\
  2591. The HELP command gives short descriptions of the send-level MS commands.\n");
  2592.   else {
  2593.     noise ("WITH");
  2594.                 /* parse a command */
  2595.     parse (&hlpfdb,&parseval,&used);
  2596.     if (used == &hlpfdb) cmxprintf ("\
  2597. You are at MS send level, which allows you to change various parts of your\n\
  2598. message prior to sending it.\n");
  2599.     else {
  2600.       void *which = (void *) parseval._pvint;
  2601.       confirm ();
  2602.       do_shelp (which,msg,body);/* dispatch to appropriate command */
  2603.     }
  2604.   }
  2605. }
  2606.  
  2607.  
  2608. /* QUIT command
  2609.  * Accepts: help flag
  2610.  *        message
  2611.  */
  2612.  
  2613. void s_quit (short help,ENVELOPE *msg,BODY *body)
  2614. {
  2615.   if (help) cmxprintf ("\
  2616. The QUIT command aborts the message being composed and returns to top level.\
  2617. \n");
  2618.   else {
  2619.     noise ("SEND LEVEL");
  2620.     confirm ();
  2621.     done = T;            /* let send level know it's time to die */
  2622.     quitted = T;        /* flag to zap answered */
  2623.   }
  2624. }
  2625.  
  2626. /* REMOVE command
  2627.  * Accepts: help flag
  2628.  *        message
  2629.  */
  2630.  
  2631. void s_remove (short help,ENVELOPE *msg,BODY *body)
  2632. {
  2633.   if (help) cmxprintf ("\
  2634. The REMOVE command removes the specified recipient.\n");
  2635.   else {
  2636.     char text[TMPLEN];
  2637.     pval parseval;
  2638.     fdb *used;
  2639.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"recipient mailbox",NIL,NIL};
  2640.     noise ("RECIPIENT");
  2641.                 /* get subject */
  2642.     parse (&linfdb,&parseval,&used);
  2643.     strcpy (text,atmbuf);    /* copy text into temp buffer */
  2644.     confirm ();
  2645.                 /* remove the recipient from all lists */
  2646.     msg->to = remove_adr (msg->to,text);
  2647.     msg->cc = remove_adr (msg->cc,text);
  2648.     msg->bcc = remove_adr (msg->bcc,text);
  2649.   }
  2650. }
  2651.  
  2652. /* SEND command
  2653.  * Accepts: help flag
  2654.  *        message
  2655.  */
  2656.  
  2657. void s_send (short help,ENVELOPE *msg,BODY *body)
  2658. {
  2659.   if (help) cmxprintf ("Send this message.\n");
  2660.   else {
  2661. #if unix
  2662.     int i = 0;
  2663.     unsigned char *text = body->contents.text;
  2664.     unsigned char *src = body->contents.text;
  2665.     unsigned char *dst;
  2666. #endif
  2667.     noise ("MESSAGE");
  2668.     confirm ();
  2669. #if unix
  2670.                 /* count LF's in string */
  2671.     while (*src) if (*src++ == '\012') i++;
  2672.     body->contents.text =     /* get space for destination string */
  2673.       (dst = (unsigned char *) fs_get (1+i + (src - body->contents.text)));
  2674.     src = text;            /* source string */
  2675.     while (*src) {        /* copy string, inserting CR's before LF's */
  2676.                 /* if CR, copy it and skip LF check for next */
  2677.       if (*src == '\015') *dst++ = *src++;
  2678.                 /* else if LF, insert a CR before it */
  2679.       else if (*src == '\012') *dst++ = '\015';
  2680.       if (*src) *dst++ = *src++;/* copy (may be null if last char was CR) */
  2681.     }
  2682.     *dst = '\0';        /* tie off destination */
  2683.     send_message (msg,body);    /* send the message */
  2684.     body->contents.text = text;    /* restore original */
  2685. #else
  2686.     send_message (msg,body);    /* send the message */
  2687. #endif
  2688.     quitted = NIL;        /* flag to zap answered */
  2689.   }
  2690. }
  2691.  
  2692. /* SUBJECT command
  2693.  * Accepts: help flag
  2694.  *        message
  2695.  */
  2696.  
  2697. void s_subject (short help,ENVELOPE *msg,BODY *body)
  2698. {
  2699.   if (help) cmxprintf ("\
  2700. The SUBJECT command sets the subject of this message.\n");
  2701.   else {
  2702.     char subject[TMPLEN];
  2703.     pval parseval;
  2704.     fdb *used;
  2705.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"subject text",NIL,NIL};
  2706.                 /* get subject */
  2707.     parse (&linfdb,&parseval,&used);
  2708.     strcpy (subject,atmbuf);    /* copy subject into temp buffer */
  2709.     confirm ();
  2710.                 /* flush the old subject */
  2711.     if (msg->subject) fs_give ((void **) &msg->subject);
  2712.                 /* set new subject */
  2713.     msg->subject = subject[0] ? cpystr (subject) : NIL;
  2714.   }
  2715. }
  2716.  
  2717. /* TO command
  2718.  * Accepts: help flag
  2719.  *        message
  2720.  */
  2721.  
  2722. void s_to (short help,ENVELOPE *msg,BODY *body)
  2723. {
  2724.   if (help) cmxprintf ("\
  2725. The TO command adds recipients to the primary recipient (to) list.\n");
  2726.   else {
  2727.     ADDRESS *adr = NIL;
  2728.     ADDRESS *lst;
  2729.     pval parseval;
  2730.     fdb *used;
  2731.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"to list",
  2732.                NIL,NIL};
  2733.     noise ("TO");
  2734.     parse (&linfdb,&parseval,&used);
  2735.                 /* free old one in case reparse */
  2736.     if (adr) mail_free_address (&adr);
  2737.                 /* parse the address list */
  2738.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  2739.     confirm ();
  2740.     if (lst = msg->to) {    /* if a to list already */
  2741.                 /* run down the list until the end */
  2742.       while (lst->next) lst = lst->next;
  2743.       lst->next = adr;        /* and link at the end of the list */
  2744.     }
  2745.     else msg->to = adr;        /* else this is the to list */
  2746.   }
  2747. }
  2748.  
  2749. /* Sequence parser  */
  2750.  
  2751.  
  2752. #define s_date 1
  2753. #define s_flag 2
  2754. #define s_string 3
  2755.  
  2756. static keywrd seqcmds[] = {
  2757.   {"ALL",    NIL,    (keyval) NIL},
  2758.   {"ANSWERED",    NIL,    (keyval) NIL},
  2759.   {"BCC",    NIL,    (keyval) s_string},
  2760.   {"BEFORE",    NIL,    (keyval) s_date},
  2761.   {"BODY",    NIL,    (keyval) s_string},
  2762.   {"CC",    NIL,    (keyval) s_string},
  2763.   {"DELETED",    NIL,    (keyval) NIL},
  2764.   {"FLAGGED",    NIL,    (keyval) NIL},
  2765.   {"FROM",    NIL,    (keyval) s_string},
  2766.   {"KEYWORD",    NIL,    (keyval) s_flag},
  2767.   {"NEW",    NIL,    (keyval) NIL},
  2768.   {"OLD",    NIL,    (keyval) NIL},
  2769.   {"ON",    NIL,    (keyval) s_date},
  2770.   {"RECENT",    NIL,    (keyval) NIL},
  2771.   {"SEEN",    NIL,    (keyval) NIL},
  2772.   {"SINCE",    NIL,    (keyval) s_date},
  2773.   {"SUBJECT",    NIL,    (keyval) s_string},
  2774.   {"TEXT",    NIL,    (keyval) s_string},
  2775.   {"TO",    NIL,    (keyval) s_string},
  2776.   {"UNANSWERED",NIL,    (keyval) NIL},
  2777.   {"UNDELETED",    NIL,    (keyval) NIL},
  2778.   {"UNFLAGGED",    NIL,    (keyval) NIL},
  2779.   {"UNKEYWORD",    NIL,    (keyval) s_flag},
  2780.   {"UNSEEN",    NIL,    (keyval) NIL},
  2781. };
  2782. static keytab seqtab = {(sizeof (seqcmds)/sizeof (keywrd)),seqcmds};
  2783.  
  2784. static keywrd seq2cmds[] = {
  2785.   {"LAST",    NIL,    (keyval) T},
  2786. };
  2787. static keytab seq2tab = {(sizeof (seq2cmds)/sizeof (keywrd)),seq2cmds};
  2788.  
  2789. /* Sequence parser
  2790.  * Accepts: default string
  2791.  * Returns: first element of the sequence as a success flag
  2792.  */
  2793.  
  2794. char do_sequence (char *def)
  2795. {
  2796.   int i,j;
  2797.   int msgno;
  2798.   char tmp[TMPLEN];
  2799.   char tmpx[TMPLEN];
  2800.   char *seq;
  2801.   pval parseval;
  2802.   fdb *used;
  2803.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(seqtab),"Message sequence, ",
  2804.              NIL,NIL};
  2805.   static fdb cm2fdb = {_CMKEY,NIL,NIL,(pdat) &(seq2tab),NIL,NIL,NIL};
  2806.   static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  2807.   static fdb numfdb = {_CMNUM,NIL,NIL,(pdat) 10,NIL,NIL,NIL};
  2808.   static fdb cfmfdb = {_CMCFM,NIL,NIL,NIL,NIL,NIL,NIL};
  2809.   static fdb datfdb = {_CMTAD,DTP_NTI,NIL,NIL,NIL,NIL};
  2810.   static fdb qstfdb = {_CMQST,NIL,NIL,NIL,NIL,NIL,NIL};
  2811.   static fdb fldfdb = {_CMFLD,NIL,NIL,NIL,NIL,NIL,NIL};
  2812.   static fdb cmafdb = {_CMTOK,CM_SDH,NIL,",","comma for another number",NIL,
  2813.              NIL};
  2814.   static fdb clnfdb = {_CMTOK,CM_SDH,NIL,":","colon for a range",NIL,NIL};
  2815.   if (!stream) {
  2816.     cmerr ("No mailbox is currently open");
  2817.     return (NIL);
  2818.   }
  2819.   qstfdb._cmlst = (fdb *) &fldfdb;
  2820.   cmdfdb._cmlst = (fdb *) &cm2fdb;
  2821.   cm2fdb._cmlst = (fdb *) &numfdb;
  2822.   cmdfdb._cmdef = def;        /* default is first whatever was in call */
  2823.                 /* else default is previous sequence if any */
  2824.   numfdb._cmdef = sequence[0] ? sequence : NIL;
  2825.   clnfdb._cmlst = (fdb *) &cmafdb;
  2826.   cmafdb._cmlst = (fdb *) &cfmfdb;
  2827.   noise ("MESSAGES");
  2828.                 /* get first sequence item */
  2829.   parse (&cmdfdb,&parseval,&used);
  2830.   if (used == &numfdb) {
  2831.                 /* invalidate the current sequence */
  2832.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  2833.     while (used != &cfmfdb) {
  2834.       i = parseval._pvint;    /* note message number */
  2835.       if (i < 1 || i > nmsgs) {
  2836.     cmerr ("Invalid message number");
  2837.     return (NIL);
  2838.       }
  2839.                 /* get what's after number */
  2840.       parse (&clnfdb,&parseval,&used);
  2841.                 /* select just this message if not range */
  2842.       if (used != &clnfdb) mail_elt (stream,i)->spare = T;
  2843.       else {            /* user wants a range */
  2844.     parse (&numfdb,&parseval,&used);
  2845.     if (parseval._pvint < 1 || parseval._pvint > nmsgs) {
  2846.       cmerr ("Invalid message number");
  2847.       return (NIL);
  2848.     }
  2849.                 /* reverse range? */
  2850.     if (i > parseval._pvint) {
  2851.       j = i;        /* yes, reverse it back again */
  2852.       i = parseval._pvint;
  2853.     }
  2854.     else j = parseval._pvint;
  2855.                 /* set range to T */
  2856.     do mail_elt (stream,i++)->spare = T;
  2857.     while (i <= j);
  2858.                 /* get what's after range */
  2859.     parse (&cmafdb,&parseval,&used);
  2860.       }
  2861.                 /* if got a comma, get another number */
  2862.       if (used == &cmafdb) parse (&numfdb,&parseval,&used);
  2863.     }
  2864.   }
  2865.   else {
  2866.     tmp[0] = '\0';        /* init search buffer */
  2867.                 /* init selection */
  2868.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = T;
  2869.                 /* numbers not allowed any more */
  2870.     cm2fdb._cmlst = (fdb *) &cfmfdb;
  2871.     do {
  2872.       if (used == &cmdfdb) {    /* command for mail_search? */
  2873.                 /* prepend a space if not the first time */
  2874.     if (tmp[0] != '\0') strcat (tmp," ");
  2875.     /* This is a disgusting kludge.  I'm ashamed to admit having written
  2876.        it, but not as much as the authors of CCMD should be for having
  2877.        cmkey parsing return the data item instead of the entire keyword
  2878.        block. */
  2879.     ucase (atmbuf);        /* strncmp is case-dependent */
  2880.                 /* locate the keyword */
  2881.     for (i = 0; strncmp (seqcmds[i]._kwkwd,atmbuf,strlen (atmbuf)); i++);
  2882.                 /* see what we need to do */
  2883.     switch (parseval._pvint) {
  2884.     case s_date:        /* slurp date */
  2885.       parse (&datfdb,&parseval,&used);
  2886.       sprintf (tmpx,"%s %d/%d/%d",seqcmds[i]._kwkwd,
  2887.            (&parseval._pvtad)->_dtmon+1,(&parseval._pvtad)->_dtday+1,
  2888.            (&parseval._pvtad)->_dtyr);
  2889.       strcat (tmp,tmpx);    /* append string */
  2890.       break;
  2891.     case s_flag:        /* slurp keyword */
  2892.       parse (&flgfdb,&parseval,&used);
  2893.       sprintf (tmpx,"%s %s",seqcmds[i]._kwkwd,(char *) parseval._pvint);
  2894.       strcat (tmp,tmpx);    /* append string */
  2895.       break;
  2896.     case s_string:        /* slurp quoted string or field */
  2897.       parse (&qstfdb,&parseval,&used);
  2898.       sprintf (tmpx,"%s \"%s\"",seqcmds[i]._kwkwd,atmbuf);
  2899.       strcat (tmp,tmpx);    /* append string */
  2900.       break;
  2901.     default:        /* command that doesn't take an argument */
  2902.       strcat (tmp,seqcmds[i]._kwkwd);
  2903.       break;
  2904.     }
  2905.       }
  2906.       else {            /* local command, LAST only one so far */
  2907.     parse (&numfdb,&parseval,&used);
  2908.     if (parseval._pvint < 1 || parseval._pvint > nmsgs) {
  2909.       cmerr ("Invalid number of messages");
  2910.       return (NIL);
  2911.     }
  2912.                 /* reject specified number of messages */
  2913.     for (i = 1; i <= stream->nmsgs - parseval._pvint; i++)
  2914.       mail_elt (stream,i)->spare = NIL;
  2915.       }
  2916.       parse (&cmdfdb,&parseval,&used);
  2917.     } while (used != &cfmfdb);    /* loop until confirm */
  2918.     sequence[current = 0] = '\0';
  2919.     if (tmp[0]) {        /* search needed? */
  2920.       mail_search (stream,tmp);
  2921.       for (i = 1; i <= nmsgs; ++i)
  2922.     mail_elt (stream,i)->spare &= mail_elt (stream,i)->searched;
  2923.     }
  2924.   }
  2925.                 /* recompute sequence string */
  2926.   seq = sequence;        /* start sequence pointer */
  2927.   *seq = '\0';            /* destroy old sequence poop */
  2928.   i = NIL;            /* delimiter is null first time through */
  2929.   for (msgno = 1; msgno <= nmsgs; ++msgno)
  2930.     if (mail_elt (stream,msgno)->spare) {
  2931.                 /* if at the last message or next not sel */
  2932.                 /* output delimiter and the number */
  2933.       sprintf (seq,"%s%d",(i ? "," : ""),msgno);
  2934.       /* The following kludge is necessary because the damn VAX C library has
  2935.      sprintf return a char * rather than an int!  The comment by it in
  2936.      stdio.h is "too painful to do right".  The cretin responsible should
  2937.      be drawn and quartered. */
  2938.       seq += (i = strlen (seq));/* update the pointer */
  2939.       current = msgno;        /* this is the new current */
  2940.                 /* any consecutive entries? */
  2941.       if (msgno < nmsgs && mail_elt (stream,msgno+1)->spare) {
  2942.                 /* yes, look for end of range */
  2943.     while (msgno < nmsgs && mail_elt (stream,msgno+1)->spare) msgno++;
  2944.     sprintf (seq,":%d",msgno);/* output delimiter and final of range */
  2945.     seq += (i = strlen (seq));/* (see above kludge note) */
  2946.     current = msgno;    /* this is the new current */
  2947.       }
  2948.     }
  2949.   return (sequence[0]);        /* flag if any sequence found */
  2950. }
  2951.  
  2952. /* Command subroutines */
  2953.  
  2954.  
  2955. /* Execute code under "more"
  2956.  * Accepts: function to be called
  2957.  *        integer argument to function
  2958.  */
  2959.  
  2960. void more (void (*f)(FILE *pipe,long arg),long arg)
  2961. {
  2962. #if unix
  2963.   FILE *pipe = popen ("more","w");
  2964.   if (pipe) {
  2965.     fflush (stdout);        /* make sure regular output is done */
  2966.     fflush (stderr);
  2967.     critical = T;        /* ignore CTRL/C while more is running */
  2968.     (*f)(pipe,arg);
  2969.     fflush (pipe);        /* wait for output to be done */
  2970.     pclose (pipe);        /* close the pipe */
  2971.     critical = NIL;        /* allow CTRL/C again */
  2972.   }
  2973.   else (*f)(stdout,arg);    /* do it this way if can't invoke "more" */
  2974. #else
  2975.   (*f)(stdout,arg);        /* non-Unix system */
  2976. #endif
  2977. }
  2978.  
  2979.  
  2980. /* Display current message
  2981.  * Accepts: file to output to
  2982.  *        integer of message pointer
  2983.  */
  2984.  
  2985. void do_display (FILE *pipe,long i)
  2986. {
  2987.   ENVELOPE *msg = (ENVELOPE *) *(void **) i;
  2988.   BODY *body = (BODY *) *++(void **) i;
  2989.   char header[8196];
  2990.   rfc822_header (header,msg,body);
  2991.   fprintf (pipe,"%s%s\n",header,body->contents.text);
  2992. }
  2993.  
  2994.  
  2995. /* Do headers of selected messages
  2996.  * Accepts: file to output to
  2997.  *        dummy
  2998.  */
  2999.  
  3000. void do_header (FILE *pipe,long i)
  3001. {
  3002.   for (i = 1; i <= nmsgs; ++i) if (mail_elt (stream,i)->spare)
  3003.     header_message (pipe,i);
  3004. }
  3005.  
  3006. /* Get a mailbox
  3007.  * Accepts: mailbox name
  3008.  */
  3009.  
  3010. void do_get (char *mailbox)
  3011. {
  3012.   register int i;
  3013.   char *s,tmp[TMPLEN],lsthst[TMPLEN];
  3014.   nmsgs = 0;            /* init number of messages */
  3015.   flgtab._ktcnt = 0;        /* re-init keyword table */
  3016.   if (stream && (s = (*stream->mailbox == '{') ? stream->mailbox :
  3017.          (((*stream->mailbox == '*')&&(stream->mailbox[1] == '{')) ?
  3018.           stream->mailbox + 1 : NIL))) {
  3019.     strcpy (lsthst,s);        /* copy last host */
  3020.     if (s = strchr (lsthst,'}')) s[1] = '\0';
  3021.   }
  3022.   else lsthst[0] = '\0';    /* no last host */
  3023.                 /* open new connection */
  3024.   if (stream = mail_open (stream,mailbox,debug ? OP_DEBUG : NIL)) {
  3025.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  3026.     sequence[current = 0] = '\0';
  3027.     nmsgs = stream->nmsgs;    /* in case mail_open "failed" */
  3028.     do_status (stream);        /* report status of mailbox */
  3029.                 /* copy keywords to our table */
  3030.     for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) {
  3031.                 /* value and keyword are the same */
  3032.       flags[i]._kwval = (keyval) (flags[i]._kwkwd = stream->user_flags[i]);
  3033.       flags[i]._kwflg = NIL;    /* no special flags */
  3034.     }
  3035.     flgtab._ktcnt = i;        /* update keyword count */
  3036.     if (*stream->mailbox == '{' || ((*stream->mailbox == '*') &&
  3037.                     (stream->mailbox[1] == '{'))) {
  3038.       strcpy (tmp,strchr (stream->mailbox,'{'));
  3039.       if (s = strchr (tmp,'}')) s[1] = '\0';
  3040.       if (strcmp (tmp,lsthst)) {/* find remote bboards */
  3041.     sprintf (lsthst,"%s*",tmp);
  3042.     mail_find (stream,lsthst);
  3043.     mail_find_bboards (stream,lsthst);
  3044.       }
  3045.     }
  3046.   }
  3047. }
  3048.  
  3049.  
  3050. /* Report status of the current mailbox
  3051.  * Accepts: stream
  3052.  */
  3053.  
  3054. void do_status (MAILSTREAM *stream)
  3055. {
  3056.   char *s = stream->mailbox;
  3057.   char *m = "BBoard";
  3058.   if (s) {            /* report status */
  3059.     if (*s == '*') s++;        /* is it a bboard? */
  3060.     else m = stream->rdonly ? "Read-only mailbox" : "Mailbox";
  3061.     cmxprintf (" %s: %s, %d messages, %d recent\n",m,s,nmsgs,stream->recent);
  3062.   }
  3063. }
  3064.  
  3065.  
  3066. /* Display version of this program */
  3067.  
  3068. void do_version ()
  3069. {
  3070. #if unix
  3071.   char tmp[TMPLEN];
  3072.   char *local;
  3073.   struct hostent *host_name;
  3074.   gethostname (tmp,TMPLEN);    /* get local name */
  3075.                 /* get it in full form */
  3076.   local = (host_name = gethostbyname (tmp)) ? host_name->h_name : tmp;
  3077.   cmxprintf (" %s",local);
  3078. #endif
  3079.   cmxprintf (" MS Distributed Mail System %s\n",version);
  3080. }
  3081.  
  3082. /* Message reading subroutines */
  3083.  
  3084.  
  3085. /* Answer a message
  3086.  * Accepts: message number
  3087.  *        answer to all flag
  3088.  */
  3089.  
  3090. void answer_message (int msgno,int allflag)
  3091. {
  3092.   char tmp[TMPLEN];
  3093.   BODY *body;
  3094.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  3095.   ENVELOPE *msg;
  3096.   if (env && env->reply_to) {    /* if reply address */
  3097.     msg = send_init ();        /* get a message block */
  3098.                 /* copy reply-to */
  3099.     msg->to = copy_adr (env->reply_to,NIL);
  3100.     if (allflag) {        /* user asked for ALL reply */
  3101.       msg->cc = copy_adr (env->to,NIL);
  3102.       copy_adr (env->cc,msg->cc);
  3103.       msg->bcc = copy_adr (env->bcc,NIL);
  3104.     }
  3105.     if (env->subject) {        /* use subject in reply */
  3106.       strncpy (tmp,env->subject,3);
  3107.       tmp[3] ='\0';        /* tie off copy of first 3 chars */
  3108.       ucase (tmp);        /* make the whole thing uppercase */
  3109.                 /* a "re:" already there? */
  3110.       if (strcmp (tmp,"RE:")) sprintf (tmp,"re: %s",env->subject);
  3111.       else sprintf (tmp,"%s",env->subject);
  3112.     }
  3113.     else sprintf (tmp,"(response to message of %s)",env->date);
  3114.     msg->subject = cpystr (tmp);/* copy desired subject */
  3115.                 /* in-reply-to is our message-id if exists */
  3116.     if (env->message_id) sprintf (tmp,"%s",env->message_id);
  3117.     else {            /* build one from other info */
  3118.       if (env->from->personal)
  3119.     sprintf (tmp,"Message of %s from %s",env->date,
  3120.          env->from->personal);
  3121.       else sprintf (tmp,"Message of %s from %s@%s",env->date,
  3122.             env->from->mailbox,env->from->host);
  3123.     }
  3124.                 /* copy in-reply-to */
  3125.     msg->in_reply_to = cpystr (tmp);
  3126.     if (body = send_text ()) {    /* get text, send message */
  3127.       quitted = NIL;
  3128.       send_level (msg,body);
  3129.       mail_free_body (&body);
  3130.       if (!quitted) {        /* unless quitted */
  3131.     sprintf (tmp,"%d",msgno);
  3132.     mail_setflag (stream,tmp,"\\Answered");
  3133.       }
  3134.     }
  3135.     mail_free_envelope (&msg);    /* flush the message */
  3136.   }
  3137. }
  3138.  
  3139. /* Output header for message
  3140.  * Accepts: file to output to
  3141.  *        message number
  3142.  */
  3143.  
  3144. void header_message (FILE *file,long msgno)
  3145. {
  3146.   long i;
  3147.   char tmp[TMPLEN],frm[FROMLEN+1];
  3148.   char flgs[5];
  3149.   char date[7];
  3150.   MESSAGECACHE *c = mail_elt (stream,msgno);
  3151.   flgs[4] = (date[6] = '\0');    /* tie off constant width strings */
  3152.                 /* make sure it's in the cache */
  3153.   mail_fetchstructure (stream,msgno,NIL);
  3154.                 /* get system flags */
  3155.                 /* If recent, then either "recent" or "new"
  3156.                    otherwise, either "seen" or "unseen" */
  3157.   flgs[0] = c->recent ? (c->seen ? 'R' : 'N') : (c->seen ? ' ' : 'U');
  3158.   flgs[1] = c->flagged ? 'F' : ' ';
  3159.   flgs[2] = c->answered ? 'A' : ' ';
  3160.   flgs[3] = c->deleted ? 'D' : ' ';
  3161.                 /* only use "dd-mmm" from date */
  3162.   if (c->day) sprintf (date,"%2d-%s",c->day,months[c->month - 1]);
  3163.   else strncpy (date,"dd-mmm",6);
  3164.   if (i = c->user_flags) {    /* first stash user flags into tmp */
  3165.     tmp[0] = '{';        /* open brace for keywords */
  3166.     tmp[1] = '\0';        /* tie off so strcat starts off right */
  3167.     while (i) {
  3168.                 /* output this keyword */
  3169.       strcat (tmp,stream->user_flags[find_rightmost_bit (&i)]);
  3170.                 /* followed by spaces until done */
  3171.       if (i) strcat (tmp," ");
  3172.     }
  3173.     strcat (tmp,"} ");        /* close brace and space before subject */
  3174.   }
  3175.   else tmp[0] = '\0';
  3176.   mail_fetchfrom (frm,stream,msgno,FROMLEN);
  3177.                 /* now append the subject */
  3178.   mail_fetchsubject (tmp + strlen (tmp),stream,msgno,SUBJECTLEN);
  3179.   tmp[SUBJECTLEN] = '\0';    /* and trim it to the subject length */
  3180.                 /* output what we got */
  3181.   fprintf (file,"%s%4d) %s %s %s (%d chars)\n",flgs,c->msgno,date,frm,
  3182.        tmp,c->rfc822_size);
  3183. }
  3184.  
  3185. /* Literal type a message
  3186.  * Accepts: file to output to
  3187.  *        message number
  3188.  */
  3189.  
  3190. void literal_type_message (FILE *file,long msgno)
  3191. {
  3192.   char c,*t,*hdr,*text;
  3193.   mail_parameters (NIL,SET_PREFETCH,(void *) T);
  3194.   hdr = cpystr (mail_fetchheader (stream,msgno));
  3195.   mail_parameters (NIL,SET_PREFETCH,NIL);
  3196.   text = mail_fetchtext (stream,msgno);
  3197.                 /* output the poop */
  3198.   fprintf (file,"Message %d of %d (%d chars)\n",msgno,nmsgs,
  3199.        strlen (hdr) + strlen (text));
  3200. #if unix
  3201.   for (t = hdr; c = *t++;) if (c != '\r') putc (c,file);
  3202.   while (c = *text++) if (c != '\r') putc (c,file);
  3203. #else
  3204.   fputs (hdr,file);
  3205.   fputs (text,file);
  3206. #endif
  3207.   putc ('\n',file);
  3208.   fs_give ((void **) &hdr);
  3209. }
  3210.  
  3211. /* Remail a message
  3212.  * Accepts: message number
  3213.  *        remail address list
  3214.  */
  3215.  
  3216. void remail_message (int msgno,ADDRESS *adr)
  3217. {
  3218.   ENVELOPE *msg = send_init ();
  3219.   BODY *body = mail_newbody ();
  3220.   msg->to = adr;        /* set to-list */
  3221.                 /* get header */
  3222.   mail_parameters (NIL,SET_PREFETCH,(void *) T);
  3223.   msg->remail = cpystr (mail_fetchheader (stream,msgno));
  3224.   mail_parameters (NIL,SET_PREFETCH,NIL);
  3225.                 /* get body of message */
  3226.   body->contents.text = (unsigned char *) cpystr(mail_fetchtext(stream,msgno));
  3227.   send_message (msg,body);    /* send off the message */
  3228.                 /* if lost, enter send-level */
  3229.   if (!done) send_level (msg,body);
  3230.   else done = NIL;        /* restore prior done state */
  3231.   msg->to = NIL;        /* be sure the address isn't nuked */
  3232.   mail_free_envelope (&msg);    /* flush the message */
  3233.   mail_free_body (&body);
  3234. }
  3235.  
  3236. /* Type a message
  3237.  * Accepts: file to output to
  3238.  *        message number
  3239.  */
  3240.  
  3241. void type_message (FILE *file,long msgno)
  3242. {
  3243.   int i,j;
  3244.   char *text;
  3245.   char c;
  3246.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  3247.                 /* make sure we get some text */
  3248.   if (text = mail_fetchtext (stream,msgno)) {
  3249.                 /* output the poop */
  3250.     fprintf (file,"Message %d of %d (%d chars)\n",msgno,nmsgs,strlen (text));
  3251.     if (env) {            /* make sure we have an envelope */
  3252.                 /* output envelope */
  3253.       if (env->date) fprintf (file,"Date: %s\n",env->date);
  3254.       type_address (file,"From",env->from);
  3255.       if (env->subject) fprintf (file,"Subject: %s\n",env->subject);
  3256.       type_address (file,"To",env->to);
  3257.       type_address (file,"cc",env->cc);
  3258.       type_address (file,"bcc",env->bcc);
  3259.     }
  3260.     putc ('\n',file);        /* output message text */
  3261. #if unix
  3262.     while (c = *text++) if (c != '\r') putc (c,file);
  3263. #else
  3264.     fputs (text,file);
  3265. #endif
  3266.     putc ('\n',file);
  3267.   }
  3268. }
  3269.  
  3270. /* Type an address
  3271.  * Accepts: file to output to
  3272.  *        tag string to start with
  3273.  *        address to output
  3274.  */
  3275.  
  3276. void type_address (FILE *file,char *tag,ADDRESS *adr)
  3277. {
  3278.   char c,tmp[8196];
  3279.   char *s = tmp;
  3280.   *s = '\0';
  3281.   rfc822_address_line (&s,tag,NIL,adr);
  3282.   for (s = tmp; c = *s++;) if (c != '\r') putc (c,file);
  3283. }
  3284.  
  3285.  
  3286. /* Type a string or string del string
  3287.  * Accepts: file to output to
  3288.  *        pointer current line position
  3289.  *        first string to output
  3290.  *        optional delimiter
  3291.  *        second string to output
  3292.  */
  3293.  
  3294. #define MAXLINE 78
  3295. void type_string (FILE *file,int *pos,char *str1,char chr,char *str2)
  3296. {
  3297.   int i;
  3298.   i = strlen (str1) + 2;    /* count up space, length of string, comma */
  3299.   if (chr) i++;            /* bump if delimiter and second string */
  3300.   if (str2) i += strlen (str2);
  3301.   if ((*pos += i) > MAXLINE) {    /* line too long? */
  3302.     fprintf (file,"\n ");    /* yes, start new line */
  3303.     *pos = i + 2;        /* reset position */
  3304.   }
  3305.   fprintf (file," %s",str1);    /* output first string */
  3306.   if (chr) fputc (chr,file);    /* delimiter and second string */
  3307.   if (str2) fprintf (file,"%s",str2);
  3308. }
  3309.  
  3310. /* Message sending subroutines */
  3311.  
  3312.  
  3313. /* Create a message composition structure
  3314.  * Returns: message structure
  3315.  */
  3316.  
  3317. ENVELOPE *send_init ()
  3318. {
  3319.   char tmp[TMPLEN];
  3320.   ENVELOPE *msg = mail_newenvelope ();
  3321.   rfc822_date (tmp);
  3322.   msg->date = cpystr (tmp);
  3323.   msg->from = mail_newaddr ();
  3324.   if (personal) msg->from->personal = cpystr (personal);
  3325.   msg->from->mailbox = cpystr (curusr);
  3326.   msg->from->host = cpystr (curhst);
  3327.   msg->return_path = mail_newaddr ();
  3328.   msg->return_path->mailbox = cpystr (curusr);
  3329.   msg->return_path->host = cpystr (curhst);
  3330.   sprintf (tmp,"<MS-C.%d.%d.%s@%s>",time (0),rand (),curusr,curhst);
  3331.   msg->message_id = cpystr (tmp);
  3332.   return (msg);
  3333. }
  3334.  
  3335. /* Copy address list
  3336.  * Accepts: MAP address list
  3337.  *        optional MTP address list to append to
  3338.  * Returns: MTP address list
  3339.  */
  3340.  
  3341. ADDRESS *copy_adr (ADDRESS *adr,ADDRESS *ret)
  3342. {
  3343.   ADDRESS *dadr;        /* current destination */
  3344.   ADDRESS *prev = ret;        /* previous destination */
  3345.   ADDRESS *tadr;
  3346.                 /* run down previous list until the end */
  3347.   if (prev) while (prev->next) prev = prev->next;
  3348.   while (adr) {            /* loop while there's still an MAP adr */
  3349.     if (adr->host) {        /* ignore group stuff */
  3350.       dadr = mail_newaddr ();    /* instantiate a new address */
  3351.       if (!ret) ret = dadr;    /* note return */
  3352.                 /* tie on to the end of any previous */
  3353.       if (prev) prev->next = dadr;
  3354.       dadr->personal = cpystr (adr->personal);
  3355.       dadr->adl = cpystr (adr->adl);
  3356.       dadr->mailbox = cpystr (adr->mailbox);
  3357.       dadr->host = cpystr (adr->host);
  3358.       prev = dadr;        /* this is now the previous */
  3359.     }
  3360.     adr = adr->next;        /* go to next address in list */
  3361.   }
  3362.   return (ret);            /* return the MTP address list */
  3363. }
  3364.  
  3365. /* Remove recipient from an address list
  3366.  * Accepts: list
  3367.  *        text of recipient
  3368.  * Returns: list (possibly zapped to NIL)
  3369.  */
  3370.  
  3371. ADDRESS *remove_adr (ADDRESS *adr,char *text)
  3372. {
  3373.   ADDRESS *ret = adr;
  3374.   ADDRESS *prev = NIL;
  3375.   while (adr) {            /* run down the list looking for this guy */
  3376.                 /* is this one we want to punt? */
  3377.     if (!strcmp (adr->mailbox,text)) {
  3378.                 /* yes, unlink this from the list */
  3379.       if (prev) prev->next = adr->next;
  3380.       else ret = adr->next;
  3381.       adr->next = NIL;        /* unlink list from this */
  3382.       mail_free_address (&adr);    /* now flush it */
  3383.                 /* get the next in line */
  3384.       adr = prev ? prev->next : ret;
  3385.     }
  3386.     else {
  3387.       prev = adr;        /* remember the previous */
  3388.       adr = adr->next;        /* try the next in line */
  3389.     }
  3390.   }
  3391.   return (ret);            /* all done */
  3392. }
  3393.  
  3394. /* Prompt for and get message text
  3395.  * Returns: message body
  3396.  */
  3397.  
  3398. BODY *send_text ()
  3399. {
  3400.   BODY *body;
  3401.   pval parseval;
  3402.   fdb *used;
  3403.   static para_data pd = {NIL,NIL};
  3404.   static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  3405.   parafdb._cmdat = (pdat) &pd;
  3406.   cmseter ();            /* set error trap */
  3407.                 /* prompt for text */
  3408.   cmxprintf (" Message (CTRL/D to send,\n\
  3409.   Use CTRL/B to insert a file, CTRL/E to enter editor, CTRL/K to redisplay\n\
  3410.   message, CTRL/L to clear screen and redisplay, CTRL/N to abort.):\n");
  3411.   cmsetrp ();            /* set reparse trap */
  3412.                 /* get the text */
  3413.   parse (¶fdb,&parseval,&used);
  3414.                 /* copy and return text if got any */
  3415.   if (parseval._pvpara == NIL) {/* aborted? */
  3416.     cmxprintf ("?Aborted\n");    /* yes, punt */
  3417.     return NIL;
  3418.   }
  3419.   cmxprintf ("^D\n");        /* give confirmation of the end */
  3420.   body = mail_newbody ();    /* make a new body */
  3421.   body->contents.text = (void *) cpystr (parseval._pvpara);
  3422.                 /* ISO-2022 stuff inside? */
  3423.   if (strstr ((char *) body->contents.text,"\033$")) {
  3424.     body->parameter = mail_newbody_parameter ();
  3425.     body->parameter->attribute = cpystr ("charset");
  3426.     body->parameter->value = cpystr ("ISO-2022-JP");
  3427.   }
  3428.   return body;
  3429. }
  3430.  
  3431. /* Send the message off
  3432.  * Accepts: message
  3433.  */
  3434.  
  3435. void send_message (ENVELOPE *msg,BODY *body)
  3436. {
  3437.   char tmp[TMPLEN];
  3438.   SMTPSTREAM *stream;
  3439.   done = T;            /* assume all is well */
  3440.   if (msg->to || msg->cc || msg->bcc) {
  3441.                 /* get MTP connection */
  3442.     if (!(stream = smtp_open (hostlist,debug))) {
  3443.       cmerr ("Can't open connection to any server");
  3444.       done = NIL;
  3445.     }
  3446.     else {            /* deliver message */
  3447.       if (!smtp_mail (stream,"MAIL",msg,body)) {
  3448.     sprintf (tmp,"Mailing failed - %s",stream->reply);
  3449.     cmerr (tmp);
  3450.     done = NIL;
  3451.       }
  3452.       smtp_close (stream);    /* close SMTP */
  3453.     }
  3454.   }
  3455.   if (done && msg->newsgroups) {/* want posting? */
  3456.                 /* get NNTP connection */
  3457.     if (!(stream = nntp_open (newslist,debug))) {
  3458.       cmerr ("Can't open connection to any news server");
  3459.       done = NIL;
  3460.     }
  3461.     else {            /* deliver message */
  3462.       if (!nntp_mail (stream,msg,body)) {
  3463.     sprintf (tmp,"Posting failed - %s",stream->reply);
  3464.     cmerr (tmp);
  3465.     done = NIL;
  3466.       }
  3467.       smtp_close (stream);    /* close NNTP */
  3468.     }
  3469.   }
  3470. }
  3471.  
  3472. /* Auxillary command parsing routines */
  3473.  
  3474.  
  3475. /* Get ON or OFF
  3476.  * Returns: NIL if OFF, T or NIL
  3477.  */
  3478.  
  3479. static keywrd onfcmds[] = {
  3480.   {"OFF",    NIL,    (keyval) NIL},
  3481.   {"ON",    NIL,    (keyval) T},
  3482. };
  3483. static keytab onftab = {(sizeof (onfcmds)/sizeof (keywrd)),onfcmds};
  3484.  
  3485. int onoff ()
  3486. {
  3487.   pval parseval;
  3488.   fdb *used;
  3489.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(onftab),"Option state, ","ON",
  3490.              NIL};
  3491.                 /* parse command */
  3492.   parse (&cmdfdb,&parseval,&used);
  3493.   return (parseval._pvint);    /* return the state */
  3494. }
  3495.  
  3496.  
  3497. /* Report an error
  3498.  * Accepts: error
  3499.  */
  3500.  
  3501. void cmerr (char *string)
  3502. {
  3503.   cmflush (cmcsb._cmij);    /* flush waiting input */
  3504.                 /* newline if necessary */
  3505.   if (cmcpos() != 0) cmnl (cmcsb._cmej);
  3506.   cmcsb._cmcol = 0;        /* make sure our counter agrees */
  3507.   cmputc ('?',cmcsb._cmej);    /* start with question mark */
  3508.   cmputs (string,cmcsb._cmej);    /* then the error string */
  3509.   cmnl (cmcsb._cmej);        /* tie off with newline */
  3510. }
  3511.  
  3512. /* Co-routines from c-client */
  3513.  
  3514.  
  3515. /* Message matches a search
  3516.  * Accepts: MAIL stream
  3517.  *        message number
  3518.  */
  3519.  
  3520. void mm_searched (MAILSTREAM *s,long msgno)
  3521. {
  3522.                 /* don't need to do anything special here */
  3523. }
  3524.  
  3525.  
  3526. /* Message exists (i.e. there are that many messages in the mailbox)
  3527.  * Accepts: MAIL stream
  3528.  *        message number
  3529.  */
  3530.  
  3531. void mm_exists (MAILSTREAM *s,long number)
  3532. {
  3533.   int delta;
  3534.   if (s != stream) return;
  3535.   if ((delta = number-nmsgs) < 0)
  3536.     cmxprintf ("%%Mailbox shrunk from %d to %d!!",nmsgs,number);
  3537.   if (nmsgs) switch (delta) {    /* no output if first time */
  3538.   case 0:            /* no new messages */
  3539.     break;
  3540.   case 1:
  3541.     cmxprintf ("[There is 1 new message]\n");
  3542.     break;
  3543.   default:
  3544.     cmxprintf ("[There are %d new messages]\n",delta);
  3545.     break;
  3546.   }
  3547.   nmsgs = number;        /* update local copy of nmsgs */
  3548. }
  3549.  
  3550.  
  3551. /* Message expunged
  3552.  * Accepts: MAIL stream
  3553.  *        message number
  3554.  */
  3555.  
  3556. void mm_expunged (MAILSTREAM *s,long number)
  3557. {
  3558.   if (s == stream) nmsgs--;    /* decrement number of messages */
  3559. }
  3560.  
  3561. /* Message flag status change
  3562.  * Accepts: MAIL stream
  3563.  *        message number
  3564.  */
  3565.  
  3566. void mm_flags (MAILSTREAM *s,long number)
  3567. {
  3568. }
  3569.  
  3570.  
  3571. /* Mailbox found
  3572.  * Accepts: Mailbox name
  3573.  */
  3574.  
  3575. void mm_mailbox (char *string)
  3576. {
  3577.   int i,j;
  3578.   for (i = 0;i < mbxtab._ktcnt && (j = strcmp (mbx[i]._kwkwd,string)) <= 0;i++)
  3579.     if (!j) return;        /* done if string already present */
  3580.   if (i == MAXMAILBOXES) cmxprintf ("%%Mailbox limit exceeded -- %s\n",string);
  3581.   else {            /* add new mailbox name */
  3582.     for (j = mbxtab._ktcnt++; i < j; j--) mbx[j] = mbx[j - 1];
  3583.     mbx[i]._kwkwd = cpystr (string);
  3584.     mbx[i]._kwflg = KEY_EMO;
  3585.     mbx[i]._kwval = NIL;
  3586.   }
  3587. }
  3588.  
  3589.  
  3590. /* BBoard found
  3591.  * Accepts: BBoard name
  3592.  */
  3593.  
  3594. void mm_bboard (char *string)
  3595. {
  3596.   int i,j;
  3597.   for (i = 0;i < bbdtab._ktcnt && (j = strcmp (bbd[i]._kwkwd,string)) <= 0;i++)
  3598.     if (!j) return;        /* done if string already present */
  3599.   if (i == MAXMAILBOXES) cmxprintf ("%%Mailbox limit exceeded -- %s\n",string);
  3600.   else {            /* add new mailbox name */
  3601.     for (j = bbdtab._ktcnt++; i < j; j--) bbd[j] = bbd[j - 1];
  3602.     bbd[i]._kwkwd = cpystr (string);
  3603.     bbd[i]._kwflg = KEY_EMO;
  3604.     bbd[i]._kwval = NIL;
  3605.   }
  3606. }
  3607.  
  3608. /* Notification event
  3609.  * Accepts: MAIL stream
  3610.  *        string to log
  3611.  *        error flag
  3612.  */
  3613.  
  3614. void mm_notify (MAILSTREAM *s,char *string,long errflg)
  3615. {
  3616.   mm_log (string,errflg);    /* just do mm_log action */
  3617. }
  3618.  
  3619.  
  3620. /* Log an event for the user to see
  3621.  * Accepts: string to log
  3622.  *        error flag
  3623.  */
  3624.  
  3625. void mm_log (char *string,long errflg)
  3626. {
  3627.   switch (errflg) {  
  3628.   case NIL:            /* no error */
  3629.     cmxprintf ("[%s]\n",string);
  3630.     break;
  3631.   case PARSE:            /* parsing problem */
  3632.   case WARN:            /* warning */
  3633.     cmxprintf ("%%%s\n",string);
  3634.     break;
  3635.   case ERROR:            /* error */
  3636.   default:
  3637.     cmxprintf ("?%s\n",string);
  3638.     break;
  3639.   }
  3640. }
  3641.  
  3642.  
  3643. /* Log an event to debugging telemetry
  3644.  * Accepts: string to log
  3645.  */
  3646.  
  3647. void mm_dlog (char *string)
  3648. {
  3649.   fprintf (stderr,"%s\n",string);
  3650. }
  3651.  
  3652. /* Get user name and password for this host
  3653.  * Accepts: host name
  3654.  *        where to return user name
  3655.  *        where to return password
  3656.  *        trial count
  3657.  */
  3658.  
  3659. void mm_login (char *host,char *username,char *password,long trial)
  3660. {
  3661.   char tmp[TMPLEN];
  3662.   pval parseval;
  3663.   fdb *used;
  3664.   static brktab usrbrk = {
  3665.     {                /* 1st char break array */
  3666.       0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  3667.       0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  3668.     },
  3669.     {                /* subsequent char break array */
  3670.                 /* same as above, plus dots */
  3671.       0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  3672.       0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  3673.     }
  3674.   };
  3675.   static brktab pswbrk = {
  3676.     {                /* 1st char break array */
  3677.       0x00,0xa4,0x25,0x10,0x00,0x00,0x00,0x00,
  3678.       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01
  3679.     },
  3680.     {                /* subsequent char break array */
  3681.       0x00,0xa4,0x25,0x10,0x00,0x00,0x00,0x00,
  3682.       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01
  3683.     }
  3684.   };
  3685.   static fdb namfdb = {_CMFLD,CM_SDH,NIL,NIL,"remote user name",NIL,&usrbrk};
  3686.   static fdb pasfdb = {_CMFLD,CM_SDH,NIL,NIL,"remote password",NIL,&pswbrk};
  3687.   namfdb._cmdef = curusr;
  3688.   if (curhst) fs_give ((void **) &curhst);
  3689.   curhst = cpystr (host);
  3690.   sprintf (tmp,"{%s} username: ",host);
  3691.   cmseter ();            /* set error trap */
  3692.   prompt (tmp);            /* prompt for and get user name */
  3693.   cmsetrp ();            /* set reparse trap */
  3694.   parse (&namfdb,&parseval,&used);
  3695.   strcpy (username,atmbuf);
  3696.   confirm ();
  3697.   if (curusr) fs_give ((void **) &curusr);
  3698.   curusr = cpystr (username);
  3699.   cmseter ();            /* set error trap */
  3700.   prompt ("Password: ");
  3701.   cmsetrp ();            /* set reparse trap */
  3702.   cmecho (NIL);            /* nuke echoing */
  3703.   parse (&pasfdb,&parseval,&used);
  3704.   strcpy (password,atmbuf);
  3705.   confirm ();
  3706.   cmxputc ('\n');        /* echo newline */
  3707.   cmecho (T);            /* re-enable echoing */
  3708.   cmnohist ();            /* clean buffers */
  3709. }
  3710.  
  3711. /* About to enter critical code
  3712.  * Accepts: stream
  3713.  */
  3714.  
  3715. void mm_critical (MAILSTREAM *s)
  3716. {
  3717.   critical = T;            /* note in critical code */
  3718. }
  3719.  
  3720.  
  3721. /* About to exit critical code
  3722.  * Accepts: stream
  3723.  */
  3724.  
  3725. void mm_nocritical (MAILSTREAM *s)
  3726. {
  3727.   critical = NIL;        /* note not in critical code */
  3728. }
  3729.  
  3730.  
  3731. /* Disk error found
  3732.  * Accepts: stream
  3733.  *        system error code
  3734.  *        flag indicating that mailbox may be clobbered
  3735.  * Returns: T if user wants to abort
  3736.  */
  3737.  
  3738. long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
  3739. {
  3740.   if (serious) cmxprintf ("[Warning: mailbox on disk is probably damaged.]\n");
  3741.   cmxprintf ("Will retry if you continue MS.\n");
  3742.   kill (getpid (),SIGSTOP);
  3743.   cmxprintf ("Retrying...\n");
  3744.   return NIL;
  3745. }
  3746.  
  3747.  
  3748. /* Log a fatal error event
  3749.  * Accepts: string to log
  3750.  */
  3751.  
  3752. void mm_fatal (char *string)
  3753. {
  3754.   mm_log (string,ERROR);    /* shouldn't happen normally */
  3755. }
  3756.